From 9aeeae54b0af3942a4ba24eeb4cb8ab60e513cfc Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Tue, 15 Jan 2019 16:56:53 -0800 Subject: [PATCH 01/83] create refactoring for converting to named parameters --- .../refactors/convertToNamedParameters.ts | 94 +++++++++++++++++++ src/services/tsconfig.json | 1 + 2 files changed, 95 insertions(+) create mode 100644 src/services/refactors/convertToNamedParameters.ts diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts new file mode 100644 index 00000000000..dd4e4326737 --- /dev/null +++ b/src/services/refactors/convertToNamedParameters.ts @@ -0,0 +1,94 @@ +/* @internal */ +namespace ts.refactor.convertToNamedParameters { + const refactorName = "Convert to named parameters"; + const refactorDescription = "Convert to named parameters"; + const actionNameNamedParameters = "Convert to named parameters"; + const actionDescriptionNamedParameters = "Convert to named parameters"; + const minimumParameterLength = 3; + const paramTypeNamePostfix = "Param"; + registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + + + function getAvailableActions(context: RefactorContext): ReadonlyArray { + const { file, startPosition } = context; + const func = getFunctionDeclarationAtPosition(file, startPosition); + if (!func) return emptyArray; + + return [{ + name: refactorName, + description: refactorDescription, + actions: [{ + name: actionNameNamedParameters, + description: actionDescriptionNamedParameters + }] + }]; + } + + function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { + Debug.assert(actionName === actionNameNamedParameters); + const { file, startPosition } = context; + const func = getFunctionDeclarationAtPosition(file, startPosition); + if (!func) return undefined; + + const paramTypeDeclaration = createParamTypeDeclaration(func); + return undefined; + } + + function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number): ValidFunctionDeclaration | undefined { + const node = getTokenAtPosition(file, startPosition); + const func = getContainingFunction(node); + if (!func || !isValidFunctionDeclaration(func)) return undefined; + return func; + } + + function isValidFunctionDeclaration(func: SignatureDeclaration): func is ValidFunctionDeclaration { + switch (func.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MethodDeclaration: + if (func.name && func.parameters && func.parameters.length > minimumParameterLength) { + return true; + } + break; + default: + } + return false; + } + + function createParamTypeDeclaration(func: ValidFunctionDeclaration): InterfaceDeclaration { + const paramTypeName = getFunctionName(func); + const paramTypeMembers = + ts.map(func.parameters, + (paramDecl, _i) => createPropertySignatureFromParameterDeclaration(paramDecl)); + return createInterfaceDeclaration( + /* decorators */ undefined, + /* modifiers */ undefined, + createIdentifier(paramTypeName), + /* type parameters */ undefined, + /* heritage clauses */ undefined, + createNodeArray(paramTypeMembers)); + } + + function getFunctionName(func: ValidFunctionDeclaration): string { + return entityNameToString(func.name) + paramTypeNamePostfix; + } + + function createPropertySignatureFromParameterDeclaration(paramDeclaration: ValidParameterDeclaration): PropertySignature { + return createPropertySignature( + /*modifiers*/ undefined, + paramDeclaration.name, + paramDeclaration.questionToken, + paramDeclaration.type, + paramDeclaration.initializer); + } + + interface ValidFunctionDeclaration extends FunctionDeclaration { + name: Identifier; + body?: FunctionBody; + typeParameters?: NodeArray; + parameters: NodeArray; + } + + interface ValidParameterDeclaration extends ParameterDeclaration { + name: Identifier; + } +} \ No newline at end of file diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 21be663055a..793089e62c8 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -84,6 +84,7 @@ "refactors/generateGetAccessorAndSetAccessor.ts", "refactors/moveToNewFile.ts", "refactors/addOrRemoveBracesToArrowFunction.ts", + "refactors/convertToNamedParameters.ts", "services.ts", "breakpoints.ts", "transform.ts", From 705ac60a592a98a09a564e1f78e9af8173cc966e Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Wed, 16 Jan 2019 15:49:09 -0800 Subject: [PATCH 02/83] WIP --- .../refactors/convertToNamedParameters.ts | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index dd4e4326737..0b5d3d265ff 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -31,7 +31,9 @@ namespace ts.refactor.convertToNamedParameters { if (!func) return undefined; const paramTypeDeclaration = createParamTypeDeclaration(func); - return undefined; + const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, paramTypeDeclaration)); + // return undefined; + return { renameFilename: undefined, renameLocation: undefined, edits }; } function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number): ValidFunctionDeclaration | undefined { @@ -45,31 +47,36 @@ namespace ts.refactor.convertToNamedParameters { switch (func.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: - if (func.name && func.parameters && func.parameters.length > minimumParameterLength) { - return true; - } - break; + case SyntaxKind.Constructor: + return !!(func.name && isValidParameterNodeArray(func.parameters)); default: } return false; } + function isValidParameterNodeArray(parameters: NodeArray): boolean { + return parameters && parameters.length > minimumParameterLength && ts.every(parameters, isValidParameterDeclaration); + } + + function isValidParameterDeclaration(paramDecl: ParameterDeclaration): paramDecl is ValidParameterDeclaration { + return !paramDecl.modifiers && !paramDecl.dotDotDotToken && isIdentifier(paramDecl.name) && !paramDecl.initializer; + } + function createParamTypeDeclaration(func: ValidFunctionDeclaration): InterfaceDeclaration { const paramTypeName = getFunctionName(func); - const paramTypeMembers = - ts.map(func.parameters, - (paramDecl, _i) => createPropertySignatureFromParameterDeclaration(paramDecl)); + const paramTypeMembers = ts.map(func.parameters, createPropertySignatureFromParameterDeclaration); + return createInterfaceDeclaration( /* decorators */ undefined, /* modifiers */ undefined, createIdentifier(paramTypeName), - /* type parameters */ undefined, - /* heritage clauses */ undefined, + func.typeParameters, + /* heritageClauses */ undefined, createNodeArray(paramTypeMembers)); } function getFunctionName(func: ValidFunctionDeclaration): string { - return entityNameToString(func.name) + paramTypeNamePostfix; + return declarationNameToString(func.name) + paramTypeNamePostfix; } function createPropertySignatureFromParameterDeclaration(paramDeclaration: ValidParameterDeclaration): PropertySignature { @@ -81,8 +88,21 @@ namespace ts.refactor.convertToNamedParameters { paramDeclaration.initializer); } - interface ValidFunctionDeclaration extends FunctionDeclaration { - name: Identifier; + function createParameterObjectBindingPattern(parameters: NodeArray, paramType: TypeNode): ParameterDeclaration { + const bindingElements = ts.map(parameters, param => createBindingElement(/* dotDotDotToken */ undefined, /* propertyName */ undefined, param.name)); + const paramName = createObjectBindingPattern(bindingElements); + + return createParameter( + /* decorators */ undefined, + /* modifiers */ undefined, + /* dotDotDotToken */ undefined, + paramName, + /* questionToken */ undefined, + paramType); + } + + interface ValidFunctionDeclaration extends MethodDeclaration { + name: PropertyName; body?: FunctionBody; typeParameters?: NodeArray; parameters: NodeArray; @@ -90,5 +110,9 @@ namespace ts.refactor.convertToNamedParameters { interface ValidParameterDeclaration extends ParameterDeclaration { name: Identifier; + type: TypeNode; + dotDotDotToken: undefined; + modifiers: undefined; + initializer: undefined; } } \ No newline at end of file From 23fc65a60c6d7b5e04c162812555bfa5cace8739 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Wed, 16 Jan 2019 16:32:32 -0800 Subject: [PATCH 03/83] implement new parameter creation --- .../refactors/convertToNamedParameters.ts | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 0b5d3d265ff..aa3bf14a137 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -5,7 +5,6 @@ namespace ts.refactor.convertToNamedParameters { const actionNameNamedParameters = "Convert to named parameters"; const actionDescriptionNamedParameters = "Convert to named parameters"; const minimumParameterLength = 3; - const paramTypeNamePostfix = "Param"; registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); @@ -30,15 +29,15 @@ namespace ts.refactor.convertToNamedParameters { const func = getFunctionDeclarationAtPosition(file, startPosition); if (!func) return undefined; - const paramTypeDeclaration = createParamTypeDeclaration(func); - const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, paramTypeDeclaration)); - // return undefined; + const newParamDeclaration = createObjectParameter(func); + const edits = textChanges.ChangeTracker.with(context, t => t.replaceNodeRangeWithNodes(file, first(func.parameters), last(func.parameters), createNodeArray([newParamDeclaration]))); return { renameFilename: undefined, renameLocation: undefined, edits }; } function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number): ValidFunctionDeclaration | undefined { const node = getTokenAtPosition(file, startPosition); const func = getContainingFunction(node); + // TODO: check range if (!func || !isValidFunctionDeclaration(func)) return undefined; return func; } @@ -62,21 +61,9 @@ namespace ts.refactor.convertToNamedParameters { return !paramDecl.modifiers && !paramDecl.dotDotDotToken && isIdentifier(paramDecl.name) && !paramDecl.initializer; } - function createParamTypeDeclaration(func: ValidFunctionDeclaration): InterfaceDeclaration { - const paramTypeName = getFunctionName(func); - const paramTypeMembers = ts.map(func.parameters, createPropertySignatureFromParameterDeclaration); - - return createInterfaceDeclaration( - /* decorators */ undefined, - /* modifiers */ undefined, - createIdentifier(paramTypeName), - func.typeParameters, - /* heritageClauses */ undefined, - createNodeArray(paramTypeMembers)); - } - - function getFunctionName(func: ValidFunctionDeclaration): string { - return declarationNameToString(func.name) + paramTypeNamePostfix; + function createParamTypeNode(func: ValidFunctionDeclaration): TypeLiteralNode { + const members = ts.map(func.parameters, createPropertySignatureFromParameterDeclaration); + return createTypeLiteralNode(members); } function createPropertySignatureFromParameterDeclaration(paramDeclaration: ValidParameterDeclaration): PropertySignature { @@ -88,9 +75,10 @@ namespace ts.refactor.convertToNamedParameters { paramDeclaration.initializer); } - function createParameterObjectBindingPattern(parameters: NodeArray, paramType: TypeNode): ParameterDeclaration { - const bindingElements = ts.map(parameters, param => createBindingElement(/* dotDotDotToken */ undefined, /* propertyName */ undefined, param.name)); + function createObjectParameter(func: ValidFunctionDeclaration): ParameterDeclaration { + const bindingElements = ts.map(func.parameters, param => createBindingElement(/* dotDotDotToken */ undefined, /* propertyName */ undefined, param.name)); const paramName = createObjectBindingPattern(bindingElements); + const paramType = createParamTypeNode(func); return createParameter( /* decorators */ undefined, From 919ed79f3a2795de226ce71873821b8da7ede7e2 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Wed, 16 Jan 2019 16:32:32 -0800 Subject: [PATCH 04/83] implement new parameter creation --- .../refactors/convertToNamedParameters.ts | 50 ++++++++----------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 0b5d3d265ff..fa46f56b169 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -4,8 +4,7 @@ namespace ts.refactor.convertToNamedParameters { const refactorDescription = "Convert to named parameters"; const actionNameNamedParameters = "Convert to named parameters"; const actionDescriptionNamedParameters = "Convert to named parameters"; - const minimumParameterLength = 3; - const paramTypeNamePostfix = "Param"; + const minimumParameterLength = 1; registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); @@ -26,19 +25,21 @@ namespace ts.refactor.convertToNamedParameters { function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { Debug.assert(actionName === actionNameNamedParameters); - const { file, startPosition } = context; + const { file, startPosition, program, cancellationToken } = context; const func = getFunctionDeclarationAtPosition(file, startPosition); - if (!func) return undefined; + if (!func || !cancellationToken) return undefined; - const paramTypeDeclaration = createParamTypeDeclaration(func); - const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, paramTypeDeclaration)); - // return undefined; + const newParamDeclaration = createObjectParameter(func); + // const funcRefs = FindAllReferences.getReferenceEntriesForNode(-1, func.name, program, program.getSourceFiles(), cancellationToken); + + const edits = textChanges.ChangeTracker.with(context, t => t.replaceNodeRange(file, first(func.parameters), last(func.parameters), newParamDeclaration)); return { renameFilename: undefined, renameLocation: undefined, edits }; } function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number): ValidFunctionDeclaration | undefined { const node = getTokenAtPosition(file, startPosition); const func = getContainingFunction(node); + // TODO: check range if (!func || !isValidFunctionDeclaration(func)) return undefined; return func; } @@ -55,28 +56,18 @@ namespace ts.refactor.convertToNamedParameters { } function isValidParameterNodeArray(parameters: NodeArray): boolean { - return parameters && parameters.length > minimumParameterLength && ts.every(parameters, isValidParameterDeclaration); + return parameters && parameters.length > minimumParameterLength && every(parameters, isValidParameterDeclaration); } function isValidParameterDeclaration(paramDecl: ParameterDeclaration): paramDecl is ValidParameterDeclaration { return !paramDecl.modifiers && !paramDecl.dotDotDotToken && isIdentifier(paramDecl.name) && !paramDecl.initializer; } - function createParamTypeDeclaration(func: ValidFunctionDeclaration): InterfaceDeclaration { - const paramTypeName = getFunctionName(func); - const paramTypeMembers = ts.map(func.parameters, createPropertySignatureFromParameterDeclaration); - - return createInterfaceDeclaration( - /* decorators */ undefined, - /* modifiers */ undefined, - createIdentifier(paramTypeName), - func.typeParameters, - /* heritageClauses */ undefined, - createNodeArray(paramTypeMembers)); - } - - function getFunctionName(func: ValidFunctionDeclaration): string { - return declarationNameToString(func.name) + paramTypeNamePostfix; + function createParamTypeNode(func: ValidFunctionDeclaration): TypeLiteralNode { + const members = map(func.parameters, createPropertySignatureFromParameterDeclaration); + const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); + // TODO: add emit flags on create function in factory + return typeNode; } function createPropertySignatureFromParameterDeclaration(paramDeclaration: ValidParameterDeclaration): PropertySignature { @@ -88,16 +79,17 @@ namespace ts.refactor.convertToNamedParameters { paramDeclaration.initializer); } - function createParameterObjectBindingPattern(parameters: NodeArray, paramType: TypeNode): ParameterDeclaration { - const bindingElements = ts.map(parameters, param => createBindingElement(/* dotDotDotToken */ undefined, /* propertyName */ undefined, param.name)); + function createObjectParameter(func: ValidFunctionDeclaration): ParameterDeclaration { + const bindingElements = map(func.parameters, param => createBindingElement(/*dotDotDotToken*/ undefined, /*propertyName*/ undefined, getTextOfIdentifierOrLiteral(param.name))); const paramName = createObjectBindingPattern(bindingElements); + const paramType = createParamTypeNode(func); return createParameter( - /* decorators */ undefined, - /* modifiers */ undefined, - /* dotDotDotToken */ undefined, + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, paramName, - /* questionToken */ undefined, + /*questionToken*/ undefined, paramType); } From f3e60be8b12125c6ebf71c6eaa446223660b13dd Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Mon, 28 Jan 2019 15:51:24 -0800 Subject: [PATCH 05/83] Move function getTypeNodeIfAccessible from inferFromUsage to utilities --- src/services/codefixes/inferFromUsage.ts | 24 ------------------------ src/services/utilities.ts | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 8e5b962116d..cb4e06e9dc5 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -294,30 +294,6 @@ namespace ts.codefix { } } - function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, program: Program, host: LanguageServiceHost): TypeNode | undefined { - const checker = program.getTypeChecker(); - let typeIsAccessible = true; - const notAccessible = () => { typeIsAccessible = false; }; - const res = checker.typeToTypeNode(type, enclosingScope, /*flags*/ undefined, { - trackSymbol: (symbol, declaration, meaning) => { - // TODO: GH#18217 - typeIsAccessible = typeIsAccessible && checker.isSymbolAccessible(symbol, declaration, meaning!, /*shouldComputeAliasToMarkVisible*/ false).accessibility === SymbolAccessibility.Accessible; - }, - reportInaccessibleThisError: notAccessible, - reportPrivateInBaseOfClassExpression: notAccessible, - reportInaccessibleUniqueSymbolError: notAccessible, - moduleResolverHost: { - readFile: host.readFile, - fileExists: host.fileExists, - directoryExists: host.directoryExists, - getSourceFiles: program.getSourceFiles, - getCurrentDirectory: program.getCurrentDirectory, - getCommonSourceDirectory: program.getCommonSourceDirectory, - } - }); - return typeIsAccessible ? res : undefined; - } - function getReferences(token: PropertyName | Token, program: Program, cancellationToken: CancellationToken): ReadonlyArray { // Position shouldn't matter since token is not a SourceFile. return mapDefined(FindAllReferences.getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry => diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 2f78ef2324b..0574afe34bd 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1911,4 +1911,28 @@ namespace ts { export function getSwitchedType(caseClause: CaseClause, checker: TypeChecker): Type | undefined { return checker.getTypeAtLocation(caseClause.parent.parent.expression); } + + export function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, program: Program, host: LanguageServiceHost): TypeNode | undefined { + const checker = program.getTypeChecker(); + let typeIsAccessible = true; + const notAccessible = () => { typeIsAccessible = false; }; + const res = checker.typeToTypeNode(type, enclosingScope, /*flags*/ undefined, { + trackSymbol: (symbol, declaration, meaning) => { + // TODO: GH#18217 + typeIsAccessible = typeIsAccessible && checker.isSymbolAccessible(symbol, declaration, meaning!, /*shouldComputeAliasToMarkVisible*/ false).accessibility === SymbolAccessibility.Accessible; + }, + reportInaccessibleThisError: notAccessible, + reportPrivateInBaseOfClassExpression: notAccessible, + reportInaccessibleUniqueSymbolError: notAccessible, + moduleResolverHost: { + readFile: host.readFile, + fileExists: host.fileExists, + directoryExists: host.directoryExists, + getSourceFiles: program.getSourceFiles, + getCurrentDirectory: program.getCurrentDirectory, + getCommonSourceDirectory: program.getCommonSourceDirectory, + } + }); + return typeIsAccessible ? res : undefined; + } } From 3243b4b4f2c6d066abfd1472e8eeae527edcdd18 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Mon, 28 Jan 2019 15:52:44 -0800 Subject: [PATCH 06/83] Refactor direct function calls --- .../refactors/convertToNamedParameters.ts | 211 ++++++++++++++---- 1 file changed, 171 insertions(+), 40 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index fa46f56b169..a6d75662e47 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -10,7 +10,7 @@ namespace ts.refactor.convertToNamedParameters { function getAvailableActions(context: RefactorContext): ReadonlyArray { const { file, startPosition } = context; - const func = getFunctionDeclarationAtPosition(file, startPosition); + const func = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker()); if (!func) return emptyArray; return [{ @@ -25,32 +25,102 @@ namespace ts.refactor.convertToNamedParameters { function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { Debug.assert(actionName === actionNameNamedParameters); - const { file, startPosition, program, cancellationToken } = context; - const func = getFunctionDeclarationAtPosition(file, startPosition); + const { file, startPosition, program, cancellationToken, host } = context; + const func = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker()); if (!func || !cancellationToken) return undefined; - const newParamDeclaration = createObjectParameter(func); - // const funcRefs = FindAllReferences.getReferenceEntriesForNode(-1, func.name, program, program.getSourceFiles(), cancellationToken); - - const edits = textChanges.ChangeTracker.with(context, t => t.replaceNodeRange(file, first(func.parameters), last(func.parameters), newParamDeclaration)); + const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, cancellationToken, host, t, func)); return { renameFilename: undefined, renameLocation: undefined, edits }; } - function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number): ValidFunctionDeclaration | undefined { + function doChange(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration): void { + const newParamDeclaration = getSynthesizedDeepClone(createObjectParameter(functionDeclaration, program, host)); + changes.replaceNodeRange(sourceFile, first(functionDeclaration.parameters), last(functionDeclaration.parameters), newParamDeclaration); + + const nameNode = getFunctionDeclarationName(functionDeclaration); + const functionRefs = FindAllReferences.getReferenceEntriesForNode(-1, nameNode, program, program.getSourceFiles(), cancellationToken); + const functionCalls = getDirectFunctionCalls(functionRefs); + + forEach(functionCalls, call => { + if (call.arguments && call.arguments.length) { + const newArguments = getSynthesizedDeepClone(createArgumentObject(functionDeclaration, call)); + changes.replaceNodeRange(getSourceFileOfNode(call), first(call.arguments), last(call.arguments), newArguments); + }}); + } + + function createArgumentObject(func: ValidFunctionDeclaration, funcCall: CallExpression | NewExpression): ObjectLiteralExpression { + const properties = map(funcCall.arguments, (arg, i) => createPropertyAssignment(getParameterName(func.parameters[i]), arg)); + return createObjectLiteral(properties, /*multiLine*/ false); + } + + function getDirectFunctionCalls(referenceEntries: ReadonlyArray | undefined): ReadonlyArray { + return mapDefined(referenceEntries, (entry) => { + if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node.parent) { + const functionRef = entry.node; + const parent = functionRef.parent; + switch (parent.kind) { + // Function call (foo(...)) + case SyntaxKind.CallExpression: + const callExpression = tryCast(parent, isCallExpression); + if (callExpression && callExpression.expression === functionRef) { + return callExpression; + } + break; + // Constructor call (new Foo(...)) + case SyntaxKind.NewExpression: + const newExpression = tryCast(parent, isNewExpression); + if (newExpression && newExpression.expression === functionRef) { + return newExpression; + } + break; + // Method call (x.foo(...)) + case SyntaxKind.PropertyAccessExpression: + const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); + if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionRef) { + const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression); + if (callExpression && callExpression.expression === propertyAccessExpression) { + return callExpression; + } + } + break; + // Method call (x['foo'](...)) + case SyntaxKind.ElementAccessExpression: + const elementAccessExpression = tryCast(parent, isElementAccessExpression); + if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionRef) { + const callExpression = tryCast(elementAccessExpression.parent, isCallExpression); + if (callExpression && callExpression.expression === elementAccessExpression) { + return callExpression; + } + } + break; + } + } + return undefined; + }); + } + + function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined { const node = getTokenAtPosition(file, startPosition); const func = getContainingFunction(node); - // TODO: check range - if (!func || !isValidFunctionDeclaration(func)) return undefined; + if (!func || !isValidFunctionDeclaration(func, checker) || !rangeContainsRange(func, node) || (func.body && rangeContainsRange(func.body, node))) return undefined; return func; } - function isValidFunctionDeclaration(func: SignatureDeclaration): func is ValidFunctionDeclaration { + function isValidFunctionDeclaration(func: SignatureDeclaration, checker: TypeChecker): func is ValidFunctionDeclaration { switch (func.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: + return !!func.name && isPropertyName(func.name) && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); case SyntaxKind.Constructor: - return !!(func.name && isValidParameterNodeArray(func.parameters)); - default: + if (isClassDeclaration(func.parent)) { + return !!func.parent.name && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); + } + else { + return isVariableDeclaration(func.parent.parent) && isVarConst(func.parent.parent) && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); + } + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + return isVariableDeclaration(func.parent) && isVarConst(func.parent) && isValidParameterNodeArray(func.parameters); } return false; } @@ -60,29 +130,25 @@ namespace ts.refactor.convertToNamedParameters { } function isValidParameterDeclaration(paramDecl: ParameterDeclaration): paramDecl is ValidParameterDeclaration { - return !paramDecl.modifiers && !paramDecl.dotDotDotToken && isIdentifier(paramDecl.name) && !paramDecl.initializer; + return !paramDecl.modifiers && !paramDecl.dotDotDotToken && isIdentifier(paramDecl.name); } - function createParamTypeNode(func: ValidFunctionDeclaration): TypeLiteralNode { - const members = map(func.parameters, createPropertySignatureFromParameterDeclaration); - const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); - // TODO: add emit flags on create function in factory - return typeNode; - } - - function createPropertySignatureFromParameterDeclaration(paramDeclaration: ValidParameterDeclaration): PropertySignature { - return createPropertySignature( - /*modifiers*/ undefined, - paramDeclaration.name, - paramDeclaration.questionToken, - paramDeclaration.type, - paramDeclaration.initializer); - } - - function createObjectParameter(func: ValidFunctionDeclaration): ParameterDeclaration { - const bindingElements = map(func.parameters, param => createBindingElement(/*dotDotDotToken*/ undefined, /*propertyName*/ undefined, getTextOfIdentifierOrLiteral(param.name))); + function createObjectParameter(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): ParameterDeclaration { + const bindingElements = map( + functionDeclaration.parameters, + paramDecl => { + return createBindingElement( + /*dotDotDotToken*/ undefined, + /*propertyName*/ undefined, + getParameterName(paramDecl), + paramDecl.initializer); }); const paramName = createObjectBindingPattern(bindingElements); - const paramType = createParamTypeNode(func); + const paramType = createParamTypeNode(functionDeclaration); + + let objectInitializer: Expression | undefined; + if (every(functionDeclaration.parameters, param => !!param.initializer || !!param.questionToken)) { + objectInitializer = createObjectLiteral(); + } return createParameter( /*decorators*/ undefined, @@ -90,21 +156,86 @@ namespace ts.refactor.convertToNamedParameters { /*dotDotDotToken*/ undefined, paramName, /*questionToken*/ undefined, - paramType); + paramType, + objectInitializer); + + function createParamTypeNode(func: ValidFunctionDeclaration): TypeLiteralNode { + const members = map(func.parameters, createPropertySignatureFromParameterDeclaration); + const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); + return typeNode; + } + + function createPropertySignatureFromParameterDeclaration(paramDeclaration: ValidParameterDeclaration): PropertySignature { + let paramType = paramDeclaration.type; + if (paramDeclaration.initializer && !paramType) { + const checker = program.getTypeChecker(); + const type = checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(paramDeclaration.initializer)); + paramType = getTypeNodeIfAccessible(type, paramDeclaration, program, host); + } + return createPropertySignature( + /*modifiers*/ undefined, + paramDeclaration.name, + paramDeclaration.initializer ? createToken(SyntaxKind.QuestionToken) : paramDeclaration.questionToken, + paramType, + /*initializer*/ undefined); + } } - interface ValidFunctionDeclaration extends MethodDeclaration { - name: PropertyName; - body?: FunctionBody; - typeParameters?: NodeArray; + function getParameterName(paramDecl: ValidParameterDeclaration): string { + return getTextOfIdentifierOrLiteral(paramDecl.name); + } + + function getFunctionDeclarationName(functionDeclaration: ValidFunctionDeclaration): Node { + switch (functionDeclaration.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MethodDeclaration: + return functionDeclaration.name; + case SyntaxKind.Constructor: + switch (functionDeclaration.parent.kind) { + case SyntaxKind.ClassDeclaration: + return functionDeclaration.parent.name; + case SyntaxKind.ClassExpression: + return functionDeclaration.parent.parent.name; + default: return Debug.assertNever(functionDeclaration.parent); + } + case SyntaxKind.ArrowFunction: + case SyntaxKind.FunctionExpression: + return functionDeclaration.parent.name; + } + } + + interface ValidConstructor extends ConstructorDeclaration { + parent: (ClassDeclaration & { name: Identifier }) | (ClassExpression & { parent: VariableDeclaration }); + parameters: NodeArray; + body: FunctionBody; + } + + interface ValidFunction extends FunctionDeclaration { + name: Identifier; + parameters: NodeArray; + body: FunctionBody; + } + + interface ValidMethod extends MethodDeclaration { + parameters: NodeArray; + body: FunctionBody; + } + + interface ValidFunctionExpression extends FunctionExpression { + parent: VariableDeclaration; parameters: NodeArray; } + interface ValidArrowFunction extends ArrowFunction { + parent: VariableDeclaration; + parameters: NodeArray; + } + + type ValidFunctionDeclaration = ValidConstructor | ValidFunction | ValidMethod | ValidArrowFunction | ValidFunctionExpression; + interface ValidParameterDeclaration extends ParameterDeclaration { name: Identifier; - type: TypeNode; dotDotDotToken: undefined; modifiers: undefined; - initializer: undefined; } } \ No newline at end of file From b668e342c449d70002bfcfb5ed6d3eb26c684cbd Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Mon, 28 Jan 2019 15:53:39 -0800 Subject: [PATCH 07/83] Add tests for convert to named parameters refactor --- .../refactorConvertToNamedParameters.ts | 4 ++- .../refactorConvertToNamedParameters1.ts | 23 ++++++++++++++++ .../refactorConvertToNamedParameters10.ts | 7 +++++ .../refactorConvertToNamedParameters11.ts | 13 +++++++++ .../refactorConvertToNamedParameters12.ts | 13 +++++++++ .../refactorConvertToNamedParameters13.ts | 7 +++++ .../refactorConvertToNamedParameters14.ts | 17 ++++++++++++ .../refactorConvertToNamedParameters15.ts | 8 ++++++ .../refactorConvertToNamedParameters16.ts | 23 ++++++++++++++++ .../refactorConvertToNamedParameters17.ts | 17 ++++++++++++ .../refactorConvertToNamedParameters2.ts | 27 +++++++++++++++++++ .../refactorConvertToNamedParameters3.ts | 21 +++++++++++++++ .../refactorConvertToNamedParameters4.ts | 17 ++++++++++++ .../refactorConvertToNamedParameters5.ts | 23 ++++++++++++++++ .../refactorConvertToNamedParameters6.ts | 17 ++++++++++++ .../refactorConvertToNamedParameters7.ts | 17 ++++++++++++ .../refactorConvertToNamedParameters8.ts | 17 ++++++++++++ .../refactorConvertToNamedParameters9.ts | 10 +++++++ 18 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters1.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters10.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters11.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters12.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters13.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters14.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters15.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters16.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters17.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters2.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters3.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters4.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters5.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters6.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters7.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters8.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters9.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters.ts b/tests/cases/fourslash/refactorConvertToNamedParameters.ts index e160dd471c0..0834bf288fc 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters.ts @@ -3,6 +3,7 @@ ////function f(/*a*/a: number, b: string/*b*/): string { //// return b; ////} +////f(4, "b"); goTo.select("a", "b"); edit.applyRefactor({ @@ -11,5 +12,6 @@ edit.applyRefactor({ actionDescription: "Convert to named parameters", newContent: `function f({ a, b }: { a: number; b: string; }): string { return b; -}` +} +f({ a: 4, b: "b" });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters1.ts b/tests/cases/fourslash/refactorConvertToNamedParameters1.ts new file mode 100644 index 00000000000..d7dce948cae --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters1.ts @@ -0,0 +1,23 @@ +/// + +////class Foo { +//// /*a*/bar/*b*/(t: string, s: string): string { +//// return s + t; +//// } +////} +////var foo = new Foo(); +////foo.bar("a", "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + bar({ t, s }: { t: string; s: string; }): string { + return s + t; + } +} +var foo = new Foo(); +foo.bar({ t: "a", s: "b" });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters10.ts b/tests/cases/fourslash/refactorConvertToNamedParameters10.ts new file mode 100644 index 00000000000..209a06d5bfa --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters10.ts @@ -0,0 +1,7 @@ +/// + +////const { foo, bar } = { foo: /*a*/(a: number, b: number)/*b*/ => {}, bar: () => {} }; +////foo(1, 2); + +goTo.select("a", "b"); +verify.not.refactorAvailable("Convert to named parameters"); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters11.ts b/tests/cases/fourslash/refactorConvertToNamedParameters11.ts new file mode 100644 index 00000000000..cbacbf1f21a --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters11.ts @@ -0,0 +1,13 @@ +/// + +////const foo = /*a*/function/*b*/(a: number, b: number) {}; +////foo(1, 2); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `const foo = function({ a, b }: { a: number; b: number; }) {}; +foo({ a: 1, b: 2 });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters12.ts b/tests/cases/fourslash/refactorConvertToNamedParameters12.ts new file mode 100644 index 00000000000..6a2fbdc6f04 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters12.ts @@ -0,0 +1,13 @@ +/// + +////const foo = /*a*/(a: number, b: number)/*b*/ => {}; +////foo(1, 2); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `const foo = ({ a, b }: { a: number; b: number; }) => {}; +foo({ a: 1, b: 2 });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters13.ts b/tests/cases/fourslash/refactorConvertToNamedParameters13.ts new file mode 100644 index 00000000000..ec25b50b067 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters13.ts @@ -0,0 +1,7 @@ +/// + +////var foo = /*a*/(a: number, b: number)/*b*/ => {}; +////foo(1, 2); + +goTo.select("a", "b"); +verify.not.refactorAvailable("Convert to named parameters"); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters14.ts b/tests/cases/fourslash/refactorConvertToNamedParameters14.ts new file mode 100644 index 00000000000..a7914bc9f1f --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters14.ts @@ -0,0 +1,17 @@ +/// + +////const c = class { +//// constructor(/*a*/a: number, b = { x: 1 }/*b*/) {} +////} +////var x = new c(2); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `const c = class { + constructor({ a, b = { x: 1 } }: { a: number; b?: { x: number; }; }) {} +} +var x = new c({ a: 2 });` +}); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters15.ts b/tests/cases/fourslash/refactorConvertToNamedParameters15.ts new file mode 100644 index 00000000000..a056a82871b --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters15.ts @@ -0,0 +1,8 @@ +/// + +/////export default class { +//// constructor(/*a*/a: number, b = { x: 1 }/*b*/) {} +////} + +goTo.select("a", "b"); +verify.not.refactorAvailable("Convert to named parameters"); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters16.ts b/tests/cases/fourslash/refactorConvertToNamedParameters16.ts new file mode 100644 index 00000000000..7d626215572 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters16.ts @@ -0,0 +1,23 @@ +/// + +////class Foo { +//// /*a*/bar/*b*/(t: T, s: T) { +//// return s; +//// } +////} +////var foo = new Foo(); +////foo.bar("a", "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + bar({ t, s }: { t: T; s: T; }) { + return s; + } +} +var foo = new Foo(); +foo.bar({ t: "a", s: "b" });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters17.ts b/tests/cases/fourslash/refactorConvertToNamedParameters17.ts new file mode 100644 index 00000000000..5af97ca694a --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters17.ts @@ -0,0 +1,17 @@ +/// + +////function foo(/*a*/t: T, s: S/*b*/) { +//// return s; +////} +////foo("a", "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function foo({ t, s }: { t: T; s: S; }) { + return s; +} +foo({ t: "a", s: "b" });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters2.ts b/tests/cases/fourslash/refactorConvertToNamedParameters2.ts new file mode 100644 index 00000000000..86b1f9eb610 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters2.ts @@ -0,0 +1,27 @@ +/// + +////class Foo { +//// t: string; +//// s: string; +//// /*a*/constructor/*b*/(t: string, s: string) { +//// this.t = t; +//// this.s = s; +//// } +////} +////var foo = new Foo("a", "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + t: string; + s: string; + constructor({ t, s }: { t: string; s: string; }) { + this.t = t; + this.s = s; + } +} +var foo = new Foo({ t: "a", s: "b" });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters3.ts b/tests/cases/fourslash/refactorConvertToNamedParameters3.ts new file mode 100644 index 00000000000..c84c1aa20d4 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters3.ts @@ -0,0 +1,21 @@ +/// + +////class Foo { +//// static /*a*/bar/*b*/(t: string, s: string): string { +//// return s + t; +//// } +////} +////Foo.bar("a", "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + static bar({ t, s }: { t: string; s: string; }): string { + return s + t; + } +} +Foo.bar({ t: "a", s: "b" });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters4.ts b/tests/cases/fourslash/refactorConvertToNamedParameters4.ts new file mode 100644 index 00000000000..b6bdb7a88c2 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters4.ts @@ -0,0 +1,17 @@ +/// + +////function f(/*a*/a: number, b = { x: 1, z: { s: true } }/*b*/) { +//// return b; +////} +////f(2); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function f({ a, b = { x: 1, z: { s: true } } }: { a: number; b?: { x: number; z: { s: boolean; }; }; }) { + return b; +} +f({ a: 2 });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters5.ts b/tests/cases/fourslash/refactorConvertToNamedParameters5.ts new file mode 100644 index 00000000000..8b8958bc674 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters5.ts @@ -0,0 +1,23 @@ +/// + +////class Foo { +//// /*a*/bar/*b*/(t: string, s: string): string { +//// return s + t; +//// } +////} +////var foo = new Foo(); +////foo['bar']("a", "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + bar({ t, s }: { t: string; s: string; }): string { + return s + t; + } +} +var foo = new Foo(); +foo['bar']({ t: "a", s: "b" });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters6.ts b/tests/cases/fourslash/refactorConvertToNamedParameters6.ts new file mode 100644 index 00000000000..1e63f44fc35 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters6.ts @@ -0,0 +1,17 @@ +/// + +////function f(/*a*/a: number, b: string = "1"/*b*/): string { +//// return b; +////} +////f(4, "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function f({ a, b = "1" }: { a: number; b?: string; }): string { + return b; +} +f({ a: 4, b: "b" });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters7.ts b/tests/cases/fourslash/refactorConvertToNamedParameters7.ts new file mode 100644 index 00000000000..825b34c303d --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters7.ts @@ -0,0 +1,17 @@ +/// + +////function f(/*a*/a?: number, b: string = "1"/*b*/): string { +//// return b; +////} +////f(); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function f({ a, b = "1" }: { a?: number; b?: string; } = {}): string { + return b; +} +f();` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters8.ts b/tests/cases/fourslash/refactorConvertToNamedParameters8.ts new file mode 100644 index 00000000000..2c3d7de339f --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters8.ts @@ -0,0 +1,17 @@ +/// + +////function f(/*a*/a: number, b = 1/*b*/) { +//// return b; +////} +////f(2); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function f({ a, b = 1 }: { a: number; b?: number; }) { + return b; +} +f({ a: 2 });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters9.ts b/tests/cases/fourslash/refactorConvertToNamedParameters9.ts new file mode 100644 index 00000000000..772f8ea8848 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters9.ts @@ -0,0 +1,10 @@ +/// + +////function f(a: number, b: number); +////function f(/*a*/a: number, b = 1/*b*/) { +//// return b; +////} +////f(2); + +goTo.select("a", "b"); +verify.not.refactorAvailable("Convert to named parameters"); \ No newline at end of file From 1d94322ea074b716a5ee51c099317816a497a1e5 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Tue, 29 Jan 2019 16:44:30 -0800 Subject: [PATCH 08/83] preserve this parameter when refactoring --- .../refactors/convertToNamedParameters.ts | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index a6d75662e47..3e7e2ca91ed 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -34,8 +34,9 @@ namespace ts.refactor.convertToNamedParameters { } function doChange(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration): void { - const newParamDeclaration = getSynthesizedDeepClone(createObjectParameter(functionDeclaration, program, host)); - changes.replaceNodeRange(sourceFile, first(functionDeclaration.parameters), last(functionDeclaration.parameters), newParamDeclaration); + const newParamDeclaration = map(createObjectParameter(functionDeclaration, program, host), node => getSynthesizedDeepClone(node, /*includeTrivia*/ false)); + const newFunctionDeclaration = updateDeclarationParameters(functionDeclaration, createNodeArray(newParamDeclaration)); + changes.replaceNode(sourceFile, functionDeclaration, newFunctionDeclaration); const nameNode = getFunctionDeclarationName(functionDeclaration); const functionRefs = FindAllReferences.getReferenceEntriesForNode(-1, nameNode, program, program.getSourceFiles(), cancellationToken); @@ -48,8 +49,15 @@ namespace ts.refactor.convertToNamedParameters { }}); } + function updateDeclarationParameters(declaration: SignatureDeclaration, parameters: NodeArray): SignatureDeclaration { + const newDeclaration = getSynthesizedClone(declaration); + newDeclaration.parameters = parameters; + return updateNode(newDeclaration, declaration); + } + function createArgumentObject(func: ValidFunctionDeclaration, funcCall: CallExpression | NewExpression): ObjectLiteralExpression { - const properties = map(funcCall.arguments, (arg, i) => createPropertyAssignment(getParameterName(func.parameters[i]), arg)); + const parameters = getRefactorableParameters(func.parameters); + const properties = map(funcCall.arguments, (arg, i) => createPropertyAssignment(getParameterName(parameters[i]), arg)); return createObjectLiteral(properties, /*multiLine*/ false); } @@ -126,16 +134,35 @@ namespace ts.refactor.convertToNamedParameters { } function isValidParameterNodeArray(parameters: NodeArray): boolean { - return parameters && parameters.length > minimumParameterLength && every(parameters, isValidParameterDeclaration); + return parameters && getRefactorableParametersLength(parameters) > minimumParameterLength && every(parameters, isValidParameterDeclaration); } function isValidParameterDeclaration(paramDecl: ParameterDeclaration): paramDecl is ValidParameterDeclaration { return !paramDecl.modifiers && !paramDecl.dotDotDotToken && isIdentifier(paramDecl.name); } - function createObjectParameter(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): ParameterDeclaration { + function hasThisParameter(parameters: NodeArray): boolean { + return isThis(parameters[0].name); + } + + function getRefactorableParametersLength(parameters: NodeArray): number { + if (hasThisParameter(parameters)) { + return parameters.length - 1; + } + return parameters.length; + } + + function getRefactorableParameters(parameters: NodeArray): NodeArray { + if (hasThisParameter(parameters)) { + parameters = createNodeArray(parameters.slice(1), parameters.hasTrailingComma); + } + return parameters; + } + + function createObjectParameter(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray { + const refactorableParameters = getRefactorableParameters(functionDeclaration.parameters); const bindingElements = map( - functionDeclaration.parameters, + refactorableParameters, paramDecl => { return createBindingElement( /*dotDotDotToken*/ undefined, @@ -143,14 +170,14 @@ namespace ts.refactor.convertToNamedParameters { getParameterName(paramDecl), paramDecl.initializer); }); const paramName = createObjectBindingPattern(bindingElements); - const paramType = createParamTypeNode(functionDeclaration); + const paramType = createParamTypeNode(refactorableParameters); let objectInitializer: Expression | undefined; - if (every(functionDeclaration.parameters, param => !!param.initializer || !!param.questionToken)) { + if (every(refactorableParameters, param => !!param.initializer || !!param.questionToken)) { objectInitializer = createObjectLiteral(); } - return createParameter( + const newParameter = createParameter( /*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, @@ -159,8 +186,13 @@ namespace ts.refactor.convertToNamedParameters { paramType, objectInitializer); - function createParamTypeNode(func: ValidFunctionDeclaration): TypeLiteralNode { - const members = map(func.parameters, createPropertySignatureFromParameterDeclaration); + if (hasThisParameter(functionDeclaration.parameters)) { + return createNodeArray([functionDeclaration.parameters[0], newParameter]); + } + return createNodeArray([newParameter]); + + function createParamTypeNode(parameters: NodeArray): TypeLiteralNode { + const members = map(parameters, createPropertySignatureFromParameterDeclaration); const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); return typeNode; } From 050c70a4c08d322bdae49b3489d7e7a9e56d7fa8 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Tue, 29 Jan 2019 16:45:52 -0800 Subject: [PATCH 09/83] update tests formatting --- tests/cases/fourslash/refactorConvertToNamedParameters11.ts | 4 ++-- tests/cases/fourslash/refactorConvertToNamedParameters12.ts | 4 ++-- tests/cases/fourslash/refactorConvertToNamedParameters14.ts | 4 ++-- tests/cases/fourslash/refactorConvertToNamedParameters17.ts | 2 +- tests/cases/fourslash/refactorConvertToNamedParameters5.ts | 4 +++- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters11.ts b/tests/cases/fourslash/refactorConvertToNamedParameters11.ts index cbacbf1f21a..e8aa6a92dd8 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters11.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters11.ts @@ -1,6 +1,6 @@ /// -////const foo = /*a*/function/*b*/(a: number, b: number) {}; +////const foo = /*a*/function/*b*/(a: number, b: number) { }; ////foo(1, 2); goTo.select("a", "b"); @@ -8,6 +8,6 @@ edit.applyRefactor({ refactorName: "Convert to named parameters", actionName: "Convert to named parameters", actionDescription: "Convert to named parameters", - newContent: `const foo = function({ a, b }: { a: number; b: number; }) {}; + newContent: `const foo = function({ a, b }: { a: number; b: number; }) { }; foo({ a: 1, b: 2 });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters12.ts b/tests/cases/fourslash/refactorConvertToNamedParameters12.ts index 6a2fbdc6f04..6a5a8e328a5 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters12.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters12.ts @@ -1,6 +1,6 @@ /// -////const foo = /*a*/(a: number, b: number)/*b*/ => {}; +////const foo = /*a*/(a: number, b: number)/*b*/ => { }; ////foo(1, 2); goTo.select("a", "b"); @@ -8,6 +8,6 @@ edit.applyRefactor({ refactorName: "Convert to named parameters", actionName: "Convert to named parameters", actionDescription: "Convert to named parameters", - newContent: `const foo = ({ a, b }: { a: number; b: number; }) => {}; + newContent: `const foo = ({ a, b }: { a: number; b: number; }) => { }; foo({ a: 1, b: 2 });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters14.ts b/tests/cases/fourslash/refactorConvertToNamedParameters14.ts index a7914bc9f1f..ee4b6051756 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters14.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters14.ts @@ -1,7 +1,7 @@ /// ////const c = class { -//// constructor(/*a*/a: number, b = { x: 1 }/*b*/) {} +//// constructor(/*a*/a: number, b = { x: 1 }/*b*/) { } ////} ////var x = new c(2); @@ -11,7 +11,7 @@ edit.applyRefactor({ actionName: "Convert to named parameters", actionDescription: "Convert to named parameters", newContent: `const c = class { - constructor({ a, b = { x: 1 } }: { a: number; b?: { x: number; }; }) {} + constructor({ a, b = { x: 1 } }: { a: number; b?: { x: number; }; }) { } } var x = new c({ a: 2 });` }); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters17.ts b/tests/cases/fourslash/refactorConvertToNamedParameters17.ts index 5af97ca694a..7888c33f4e2 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters17.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters17.ts @@ -11,7 +11,7 @@ edit.applyRefactor({ actionName: "Convert to named parameters", actionDescription: "Convert to named parameters", newContent: `function foo({ t, s }: { t: T; s: S; }) { - return s; + return s; } foo({ t: "a", s: "b" });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters5.ts b/tests/cases/fourslash/refactorConvertToNamedParameters5.ts index 8b8958bc674..f84e0d7b023 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters5.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters5.ts @@ -7,6 +7,7 @@ ////} ////var foo = new Foo(); ////foo['bar']("a", "b"); +////foo.bar("a", "b"); goTo.select("a", "b"); edit.applyRefactor({ @@ -19,5 +20,6 @@ edit.applyRefactor({ } } var foo = new Foo(); -foo['bar']({ t: "a", s: "b" });` +foo['bar']({ t: "a", s: "b" }); +foo.bar({ t: "a", s: "b" });` }); \ No newline at end of file From 40987ecf4196b83739800d99b28bb9a405f91be5 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Tue, 29 Jan 2019 16:46:20 -0800 Subject: [PATCH 10/83] add tests --- .../refactorConvertToNamedParameters18.ts | 17 +++++++++++++ .../refactorConvertToNamedParameters19.ts | 24 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters18.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters19.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters18.ts b/tests/cases/fourslash/refactorConvertToNamedParameters18.ts new file mode 100644 index 00000000000..069182e20e5 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters18.ts @@ -0,0 +1,17 @@ +/// + +////function foo(this: void, /*a*/t: string, s: string/*b*/) { +//// return s; +////} +////foo("a", "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function foo(this: void, { t, s }: { t: string; s: string; }) { + return s; +} +foo({ t: "a", s: "b" });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters19.ts b/tests/cases/fourslash/refactorConvertToNamedParameters19.ts new file mode 100644 index 00000000000..b635ce5e379 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters19.ts @@ -0,0 +1,24 @@ +/// + +////class Foo { +//// /*a*/bar/*b*/(t: string, s: string): string { +//// return s + t; +//// } +////} +////var foo = {}; +////foo['bar']("a", "b"); +/// + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + bar({ t, s }: { t: string; s: string; }): string { + return s + t; + } +} +var foo = {}; +foo['bar']("a", "b");` +}); \ No newline at end of file From bf25ba46509d30aff81cb528273d0111e02c2516 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Fri, 1 Feb 2019 13:09:53 -0800 Subject: [PATCH 11/83] Don't refactor if variable declaration has type annotation --- .../refactors/convertToNamedParameters.ts | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 3e7e2ca91ed..114874397a9 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -34,12 +34,12 @@ namespace ts.refactor.convertToNamedParameters { } function doChange(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration): void { - const newParamDeclaration = map(createObjectParameter(functionDeclaration, program, host), node => getSynthesizedDeepClone(node, /*includeTrivia*/ false)); - const newFunctionDeclaration = updateDeclarationParameters(functionDeclaration, createNodeArray(newParamDeclaration)); + const newParamDeclaration = createObjectParameter(functionDeclaration, program, host); + const newFunctionDeclaration = getSynthesizedDeepClone(updateDeclarationParameters(functionDeclaration, createNodeArray(newParamDeclaration)), /*includeTrivia*/ false); changes.replaceNode(sourceFile, functionDeclaration, newFunctionDeclaration); - const nameNode = getFunctionDeclarationName(functionDeclaration); - const functionRefs = FindAllReferences.getReferenceEntriesForNode(-1, nameNode, program, program.getSourceFiles(), cancellationToken); + const nameNodes = getFunctionDeclarationNames(functionDeclaration); + const functionRefs = flatMap(nameNodes, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); const functionCalls = getDirectFunctionCalls(functionRefs); forEach(functionCalls, call => { @@ -124,11 +124,11 @@ namespace ts.refactor.convertToNamedParameters { return !!func.parent.name && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); } else { - return isVariableDeclaration(func.parent.parent) && isVarConst(func.parent.parent) && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); + return isVariableDeclaration(func.parent.parent) && !func.parent.parent.type && isVarConst(func.parent.parent) && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); } case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - return isVariableDeclaration(func.parent) && isVarConst(func.parent) && isValidParameterNodeArray(func.parameters); + return isVariableDeclaration(func.parent) && !func.parent.type && isVarConst(func.parent) && isValidParameterNodeArray(func.parameters); } return false; } @@ -217,27 +217,29 @@ namespace ts.refactor.convertToNamedParameters { return getTextOfIdentifierOrLiteral(paramDecl.name); } - function getFunctionDeclarationName(functionDeclaration: ValidFunctionDeclaration): Node { + function getFunctionDeclarationNames(functionDeclaration: ValidFunctionDeclaration): Node[] { switch (functionDeclaration.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: - return functionDeclaration.name; + return [functionDeclaration.name]; case SyntaxKind.Constructor: switch (functionDeclaration.parent.kind) { case SyntaxKind.ClassDeclaration: - return functionDeclaration.parent.name; + return [functionDeclaration, functionDeclaration.parent.name]; case SyntaxKind.ClassExpression: - return functionDeclaration.parent.parent.name; + return [functionDeclaration.parent.parent.name]; default: return Debug.assertNever(functionDeclaration.parent); } case SyntaxKind.ArrowFunction: case SyntaxKind.FunctionExpression: - return functionDeclaration.parent.name; + return [functionDeclaration.parent.name]; } } + type ValidVariableDeclaration = VariableDeclaration & { type: undefined }; + interface ValidConstructor extends ConstructorDeclaration { - parent: (ClassDeclaration & { name: Identifier }) | (ClassExpression & { parent: VariableDeclaration }); + parent: (ClassDeclaration & { name: Identifier }) | (ClassExpression & { parent: ValidVariableDeclaration }); parameters: NodeArray; body: FunctionBody; } @@ -254,12 +256,12 @@ namespace ts.refactor.convertToNamedParameters { } interface ValidFunctionExpression extends FunctionExpression { - parent: VariableDeclaration; + parent: ValidVariableDeclaration; parameters: NodeArray; } interface ValidArrowFunction extends ArrowFunction { - parent: VariableDeclaration; + parent: ValidVariableDeclaration; parameters: NodeArray; } From bde97d1226b730b2ec4454e5317c7e5fb6184bae Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Fri, 1 Feb 2019 16:43:44 -0800 Subject: [PATCH 12/83] fix refactor to find super references --- src/services/refactors/convertToNamedParameters.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 114874397a9..cbc66749f50 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -40,7 +40,7 @@ namespace ts.refactor.convertToNamedParameters { const nameNodes = getFunctionDeclarationNames(functionDeclaration); const functionRefs = flatMap(nameNodes, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); - const functionCalls = getDirectFunctionCalls(functionRefs); + const functionCalls = deduplicate(getDirectFunctionCalls(functionRefs), (a, b) => a === b); forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { @@ -223,13 +223,19 @@ namespace ts.refactor.convertToNamedParameters { case SyntaxKind.MethodDeclaration: return [functionDeclaration.name]; case SyntaxKind.Constructor: + const ctrKeyword = findChildOfKind(functionDeclaration, SyntaxKind.ConstructorKeyword, functionDeclaration.getSourceFile()); + let name: Node; switch (functionDeclaration.parent.kind) { case SyntaxKind.ClassDeclaration: - return [functionDeclaration, functionDeclaration.parent.name]; + name = functionDeclaration.parent.name; + break; case SyntaxKind.ClassExpression: - return [functionDeclaration.parent.parent.name]; + name = functionDeclaration.parent.parent.name; + break; default: return Debug.assertNever(functionDeclaration.parent); } + if (ctrKeyword) return [ctrKeyword, name]; + return [name]; case SyntaxKind.ArrowFunction: case SyntaxKind.FunctionExpression: return [functionDeclaration.parent.name]; From abb11550d33265ae91046754e3c1d5890070a144 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Fri, 1 Feb 2019 16:44:22 -0800 Subject: [PATCH 13/83] add more tests --- .../refactorConvertToNamedParameters20.ts | 17 +++++++++++++ .../refactorConvertToNamedParameters21.ts | 25 +++++++++++++++++++ .../refactorConvertToNamedParameters22.ts | 7 ++++++ 3 files changed, 49 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters20.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters21.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters22.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters20.ts b/tests/cases/fourslash/refactorConvertToNamedParameters20.ts new file mode 100644 index 00000000000..0fb20ec864d --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters20.ts @@ -0,0 +1,17 @@ +/// + +////function foo(/*a*/a: number, b: number/*b*/) { +//// return { bar: () => a + b }; +////} +////var x = foo(1, 2).bar(); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function foo({ a, b }: { a: number; b: number; }) { + return { bar: () => a + b }; +} +var x = foo({ a: 1, b: 2 }).bar();` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters21.ts b/tests/cases/fourslash/refactorConvertToNamedParameters21.ts new file mode 100644 index 00000000000..c5d15903ef5 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters21.ts @@ -0,0 +1,25 @@ +/// + +////class A { +//// constructor(/*a*/a: string, b: string/*b*/) { } +////} +////class B extends A { +//// constructor(a: string, b: string, c: string) { +//// super(a, b); +//// } +////} + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class A { + constructor({ a, b }: { a: string; b: string; }) { } +} +class B extends A { + constructor(a: string, b: string, c: string) { + super({ a: a, b: b }); + } +}` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters22.ts b/tests/cases/fourslash/refactorConvertToNamedParameters22.ts new file mode 100644 index 00000000000..05a0f6f54fa --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters22.ts @@ -0,0 +1,7 @@ +/// + +////const foo: (a: number, b: number) => number = /*a*/(a: number, b: number)/*b*/ => a + b; +////foo(1, 2); + +goTo.select("a", "b"); +verify.not.refactorAvailable("Convert to named parameters"); From 674800f25e00ecc0b0ace8c1cfe33f2638624ac9 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Mon, 4 Feb 2019 12:52:27 -0800 Subject: [PATCH 14/83] implement refactor for functions with a rest parameter --- .../refactors/convertToNamedParameters.ts | 63 +++++++++++-------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index cbc66749f50..0609578c869 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -44,7 +44,7 @@ namespace ts.refactor.convertToNamedParameters { forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { - const newArguments = getSynthesizedDeepClone(createArgumentObject(functionDeclaration, call)); + const newArguments = getSynthesizedDeepClone(createArgumentObject(functionDeclaration, call.arguments)); changes.replaceNodeRange(getSourceFileOfNode(call), first(call.arguments), last(call.arguments), newArguments); }}); } @@ -55,12 +55,6 @@ namespace ts.refactor.convertToNamedParameters { return updateNode(newDeclaration, declaration); } - function createArgumentObject(func: ValidFunctionDeclaration, funcCall: CallExpression | NewExpression): ObjectLiteralExpression { - const parameters = getRefactorableParameters(func.parameters); - const properties = map(funcCall.arguments, (arg, i) => createPropertyAssignment(getParameterName(parameters[i]), arg)); - return createObjectLiteral(properties, /*multiLine*/ false); - } - function getDirectFunctionCalls(referenceEntries: ReadonlyArray | undefined): ReadonlyArray { return mapDefined(referenceEntries, (entry) => { if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node.parent) { @@ -109,26 +103,26 @@ namespace ts.refactor.convertToNamedParameters { function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined { const node = getTokenAtPosition(file, startPosition); - const func = getContainingFunction(node); - if (!func || !isValidFunctionDeclaration(func, checker) || !rangeContainsRange(func, node) || (func.body && rangeContainsRange(func.body, node))) return undefined; - return func; + const functionDeclaration = getContainingFunction(node); + if (!functionDeclaration || !isValidFunctionDeclaration(functionDeclaration, checker) || !rangeContainsRange(functionDeclaration, node) || (functionDeclaration.body && rangeContainsRange(functionDeclaration.body, node))) return undefined; + return functionDeclaration; } - function isValidFunctionDeclaration(func: SignatureDeclaration, checker: TypeChecker): func is ValidFunctionDeclaration { - switch (func.kind) { + function isValidFunctionDeclaration(functionDeclaration: SignatureDeclaration, checker: TypeChecker): functionDeclaration is ValidFunctionDeclaration { + switch (functionDeclaration.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: - return !!func.name && isPropertyName(func.name) && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); + return !!functionDeclaration.name && isPropertyName(functionDeclaration.name) && isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); case SyntaxKind.Constructor: - if (isClassDeclaration(func.parent)) { - return !!func.parent.name && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); + if (isClassDeclaration(functionDeclaration.parent)) { + return !!functionDeclaration.parent.name && isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); } else { - return isVariableDeclaration(func.parent.parent) && !func.parent.parent.type && isVarConst(func.parent.parent) && isValidParameterNodeArray(func.parameters) && !!func.body && !checker.isImplementationOfOverload(func); + return isVariableDeclaration(functionDeclaration.parent.parent) && !functionDeclaration.parent.parent.type && isVarConst(functionDeclaration.parent.parent) && isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); } case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - return isVariableDeclaration(func.parent) && !func.parent.type && isVarConst(func.parent) && isValidParameterNodeArray(func.parameters); + return isVariableDeclaration(functionDeclaration.parent) && !functionDeclaration.parent.type && isVarConst(functionDeclaration.parent) && isValidParameterNodeArray(functionDeclaration.parameters); } return false; } @@ -138,7 +132,7 @@ namespace ts.refactor.convertToNamedParameters { } function isValidParameterDeclaration(paramDecl: ParameterDeclaration): paramDecl is ValidParameterDeclaration { - return !paramDecl.modifiers && !paramDecl.dotDotDotToken && isIdentifier(paramDecl.name); + return !paramDecl.modifiers && isIdentifier(paramDecl.name); } function hasThisParameter(parameters: NodeArray): boolean { @@ -159,6 +153,21 @@ namespace ts.refactor.convertToNamedParameters { return parameters; } + function createArgumentObject(functionDeclaration: ValidFunctionDeclaration, args: NodeArray): ObjectLiteralExpression { + const parameters = getRefactorableParameters(functionDeclaration.parameters); + const hasRestParameter = isRestParameter(last(parameters)); + const nonRestArguments = hasRestParameter ? args.slice(0, parameters.length - 1) : args; + const properties = map(nonRestArguments, (arg, i) => createPropertyAssignment(getParameterName(parameters[i]), arg)); + + if (hasRestParameter && args.length >= parameters.length) { + const restArguments = args.slice(parameters.length - 1); + const restProperty = createPropertyAssignment(getParameterName(last(parameters)), createArrayLiteral(restArguments)); + properties.push(restProperty); + } + + return createObjectLiteral(properties, /*multiLine*/ false); + } + function createObjectParameter(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray { const refactorableParameters = getRefactorableParameters(functionDeclaration.parameters); const bindingElements = map( @@ -168,7 +177,7 @@ namespace ts.refactor.convertToNamedParameters { /*dotDotDotToken*/ undefined, /*propertyName*/ undefined, getParameterName(paramDecl), - paramDecl.initializer); }); + isRestParameter(paramDecl) ? createArrayLiteral() : paramDecl.initializer); }); const paramName = createObjectBindingPattern(bindingElements); const paramType = createParamTypeNode(refactorableParameters); @@ -199,18 +208,23 @@ namespace ts.refactor.convertToNamedParameters { function createPropertySignatureFromParameterDeclaration(paramDeclaration: ValidParameterDeclaration): PropertySignature { let paramType = paramDeclaration.type; - if (paramDeclaration.initializer && !paramType) { - const checker = program.getTypeChecker(); - const type = checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(paramDeclaration.initializer)); - paramType = getTypeNodeIfAccessible(type, paramDeclaration, program, host); + if (!paramType && (paramDeclaration.initializer || isRestParameter(paramDeclaration))) { + paramType = getTypeNode(paramDeclaration); } + return createPropertySignature( /*modifiers*/ undefined, paramDeclaration.name, - paramDeclaration.initializer ? createToken(SyntaxKind.QuestionToken) : paramDeclaration.questionToken, + paramDeclaration.initializer || isRestParameter(paramDeclaration) ? createToken(SyntaxKind.QuestionToken) : paramDeclaration.questionToken, paramType, /*initializer*/ undefined); } + + function getTypeNode(node: Node): TypeNode | undefined { + const checker = program.getTypeChecker(); + const type = checker.getTypeAtLocation(node); + return getTypeNodeIfAccessible(type, node, program, host); + } } function getParameterName(paramDecl: ValidParameterDeclaration): string { @@ -275,7 +289,6 @@ namespace ts.refactor.convertToNamedParameters { interface ValidParameterDeclaration extends ParameterDeclaration { name: Identifier; - dotDotDotToken: undefined; modifiers: undefined; } } \ No newline at end of file From 1d1c82095ce9abef011caa521dcf0e6222a05c62 Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Mon, 4 Feb 2019 12:53:02 -0800 Subject: [PATCH 15/83] add tests for rest parameters --- .../refactorConvertToNamedParameters23.ts | 15 +++++++++++++++ .../refactorConvertToNamedParameters24.ts | 15 +++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters23.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters24.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters23.ts b/tests/cases/fourslash/refactorConvertToNamedParameters23.ts new file mode 100644 index 00000000000..36ed5084fe2 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters23.ts @@ -0,0 +1,15 @@ +/// + +////function log(/*a*/a: number, b: number, ...args/*b*/) { } +////let l = log(-1, -2, 3, 4, 5); +////let k = log(1, 2); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function log({ a, b, args = [] }: { a: number; b: number; args?: any[]; }) { } +let l = log({ a: -1, b: -2, args: [3, 4, 5] }); +let k = log({ a: 1, b: 2 });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters24.ts b/tests/cases/fourslash/refactorConvertToNamedParameters24.ts new file mode 100644 index 00000000000..34e0331f466 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters24.ts @@ -0,0 +1,15 @@ +/// + +////function /*a*/buildName/*b*/(firstName: string, middleName?: string, ...restOfName: string[]) { } +////let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie"); +////let myName = buildName("Joseph"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function buildName({ firstName, middleName, restOfName = [] }: { firstName: string; middleName?: string; restOfName?: string[]; }) { } +let employeeName = buildName({ firstName: "Joseph", middleName: "Samuel", restOfName: ["Lucas", "MacKinzie"] }); +let myName = buildName({ firstName: "Joseph" });` +}); \ No newline at end of file From 18b2b55387a0e140d7ec5366a6821052bd46dbbf Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Wed, 6 Feb 2019 15:38:59 -0800 Subject: [PATCH 16/83] add option to suppressLeadingAndTrailingTrivia non recursively --- src/services/utilities.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 0574afe34bd..2b6d0acde88 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1753,23 +1753,33 @@ namespace ts { /** * Sets EmitFlags to suppress leading and trailing trivia on the node. */ - export function suppressLeadingAndTrailingTrivia(node: Node) { - suppressLeadingTrivia(node); - suppressTrailingTrivia(node); + export function suppressLeadingAndTrailingTrivia(node: Node, recursive = true) { + suppressLeadingTrivia(node, recursive); + suppressTrailingTrivia(node, recursive); } /** * Sets EmitFlags to suppress leading trivia on the node. */ - export function suppressLeadingTrivia(node: Node) { - addEmitFlagsRecursively(node, EmitFlags.NoLeadingComments, getFirstChild); + export function suppressLeadingTrivia(node: Node, recursive = true) { + if (recursive) { + addEmitFlagsRecursively(node, EmitFlags.NoLeadingComments, getFirstChild); + } + else { + addEmitFlags(node, EmitFlags.NoLeadingComments); + } } /** * Sets EmitFlags to suppress trailing trivia on the node. */ - export function suppressTrailingTrivia(node: Node) { - addEmitFlagsRecursively(node, EmitFlags.NoTrailingComments, getLastChild); + export function suppressTrailingTrivia(node: Node, recursive = true) { + if (recursive) { + addEmitFlagsRecursively(node, EmitFlags.NoTrailingComments, getLastChild); + } + else { + addEmitFlags(node, EmitFlags.NoTrailingComments); + } } function addEmitFlagsRecursively(node: Node, flag: EmitFlags, getChild: (n: Node) => Node | undefined) { From b87392c22c7a6ca5fb53b24243cb0b6f5b30ef7b Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Wed, 6 Feb 2019 15:40:58 -0800 Subject: [PATCH 17/83] fix duplication of leading and trailing comments on refactored function --- .../refactors/convertToNamedParameters.ts | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 0609578c869..c414f2b0a8f 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -5,6 +5,7 @@ namespace ts.refactor.convertToNamedParameters { const actionNameNamedParameters = "Convert to named parameters"; const actionDescriptionNamedParameters = "Convert to named parameters"; const minimumParameterLength = 1; + let refactorSucceeded = true; registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); @@ -26,25 +27,27 @@ namespace ts.refactor.convertToNamedParameters { function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { Debug.assert(actionName === actionNameNamedParameters); const { file, startPosition, program, cancellationToken, host } = context; - const func = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker()); - if (!func || !cancellationToken) return undefined; + const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker()); + if (!functionDeclaration || !cancellationToken) return undefined; - const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, cancellationToken, host, t, func)); - return { renameFilename: undefined, renameLocation: undefined, edits }; + const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, cancellationToken, host, t, functionDeclaration)); + return refactorSucceeded ? { renameFilename: undefined, renameLocation: undefined, edits } : undefined; } function doChange(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration): void { - const newParamDeclaration = createObjectParameter(functionDeclaration, program, host); - const newFunctionDeclaration = getSynthesizedDeepClone(updateDeclarationParameters(functionDeclaration, createNodeArray(newParamDeclaration)), /*includeTrivia*/ false); + const newParamDeclaration = map(createNewParameters(functionDeclaration, program, host), param => getSynthesizedDeepClone(param, /*includeTrivia*/ true)); + const newFunctionDeclaration = updateDeclarationParameters(functionDeclaration, createNodeArray(newParamDeclaration)); + suppressLeadingAndTrailingTrivia(newFunctionDeclaration, /*recursive*/ false); changes.replaceNode(sourceFile, functionDeclaration, newFunctionDeclaration); const nameNodes = getFunctionDeclarationNames(functionDeclaration); const functionRefs = flatMap(nameNodes, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); const functionCalls = deduplicate(getDirectFunctionCalls(functionRefs), (a, b) => a === b); + refactorSucceeded = true; // TODO: check if a bad reference was found forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { - const newArguments = getSynthesizedDeepClone(createArgumentObject(functionDeclaration, call.arguments)); + const newArguments = getSynthesizedDeepClone(createNewArguments(functionDeclaration, call.arguments), /*includeTrivia*/ true); changes.replaceNodeRange(getSourceFileOfNode(call), first(call.arguments), last(call.arguments), newArguments); }}); } @@ -125,14 +128,14 @@ namespace ts.refactor.convertToNamedParameters { return isVariableDeclaration(functionDeclaration.parent) && !functionDeclaration.parent.type && isVarConst(functionDeclaration.parent) && isValidParameterNodeArray(functionDeclaration.parameters); } return false; - } - function isValidParameterNodeArray(parameters: NodeArray): boolean { - return parameters && getRefactorableParametersLength(parameters) > minimumParameterLength && every(parameters, isValidParameterDeclaration); - } + function isValidParameterNodeArray(parameters: NodeArray): parameters is ValidParameterNodeArray { + return parameters && getRefactorableParametersLength(parameters) > minimumParameterLength && every(parameters, isValidParameterDeclaration); + } - function isValidParameterDeclaration(paramDecl: ParameterDeclaration): paramDecl is ValidParameterDeclaration { - return !paramDecl.modifiers && isIdentifier(paramDecl.name); + function isValidParameterDeclaration(paramDeclaration: ParameterDeclaration): paramDeclaration is ValidParameterDeclaration { + return !paramDeclaration.modifiers && isIdentifier(paramDeclaration.name); + } } function hasThisParameter(parameters: NodeArray): boolean { @@ -153,7 +156,7 @@ namespace ts.refactor.convertToNamedParameters { return parameters; } - function createArgumentObject(functionDeclaration: ValidFunctionDeclaration, args: NodeArray): ObjectLiteralExpression { + function createNewArguments(functionDeclaration: ValidFunctionDeclaration, args: NodeArray): ObjectLiteralExpression { const parameters = getRefactorableParameters(functionDeclaration.parameters); const hasRestParameter = isRestParameter(last(parameters)); const nonRestArguments = hasRestParameter ? args.slice(0, parameters.length - 1) : args; @@ -168,16 +171,17 @@ namespace ts.refactor.convertToNamedParameters { return createObjectLiteral(properties, /*multiLine*/ false); } - function createObjectParameter(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray { + function createNewParameters(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray { const refactorableParameters = getRefactorableParameters(functionDeclaration.parameters); const bindingElements = map( refactorableParameters, paramDecl => { - return createBindingElement( + const element = createBindingElement( /*dotDotDotToken*/ undefined, /*propertyName*/ undefined, getParameterName(paramDecl), - isRestParameter(paramDecl) ? createArrayLiteral() : paramDecl.initializer); }); + isRestParameter(paramDecl) ? createArrayLiteral() : paramDecl.initializer); + return element; }); const paramName = createObjectBindingPattern(bindingElements); const paramType = createParamTypeNode(refactorableParameters); @@ -214,7 +218,7 @@ namespace ts.refactor.convertToNamedParameters { return createPropertySignature( /*modifiers*/ undefined, - paramDeclaration.name, + getParameterName(paramDeclaration), paramDeclaration.initializer || isRestParameter(paramDeclaration) ? createToken(SyntaxKind.QuestionToken) : paramDeclaration.questionToken, paramType, /*initializer*/ undefined); @@ -227,8 +231,8 @@ namespace ts.refactor.convertToNamedParameters { } } - function getParameterName(paramDecl: ValidParameterDeclaration): string { - return getTextOfIdentifierOrLiteral(paramDecl.name); + function getParameterName(paramDeclaration: ValidParameterDeclaration) { + return getTextOfIdentifierOrLiteral(paramDeclaration.name); } function getFunctionDeclarationNames(functionDeclaration: ValidFunctionDeclaration): Node[] { @@ -256,6 +260,8 @@ namespace ts.refactor.convertToNamedParameters { } } + type ValidParameterNodeArray = NodeArray; + type ValidVariableDeclaration = VariableDeclaration & { type: undefined }; interface ValidConstructor extends ConstructorDeclaration { From 7d86fda151d48bfe6802755a0f7e407f63b747cd Mon Sep 17 00:00:00 2001 From: Gabriela Britto Date: Wed, 6 Feb 2019 16:06:46 -0800 Subject: [PATCH 18/83] fix hasThisParameter to check for parameters length --- src/services/refactors/convertToNamedParameters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index c414f2b0a8f..16716bed106 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -139,7 +139,7 @@ namespace ts.refactor.convertToNamedParameters { } function hasThisParameter(parameters: NodeArray): boolean { - return isThis(parameters[0].name); + return parameters.length > 0 && isThis(parameters[0].name); } function getRefactorableParametersLength(parameters: NodeArray): number { From 26f8ddd46a7c1dd528f1812bd647b5135118be53 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 11 Feb 2019 12:01:02 -0800 Subject: [PATCH 19/83] create functions for copying trailing comments and rename previous copyComment function --- .../codefixes/convertFunctionToEs6Class.ts | 10 ++--- .../addOrRemoveBracesToArrowFunction.ts | 4 +- src/services/utilities.ts | 43 ++++++++++++++++++- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/services/codefixes/convertFunctionToEs6Class.ts b/src/services/codefixes/convertFunctionToEs6Class.ts index 78e10ce2044..b87a78106ad 100644 --- a/src/services/codefixes/convertFunctionToEs6Class.ts +++ b/src/services/codefixes/convertFunctionToEs6Class.ts @@ -35,7 +35,7 @@ namespace ts.codefix { precedingNode = ctorDeclaration.parent.parent; newClassDeclaration = createClassFromVariableDeclaration(ctorDeclaration as VariableDeclaration); if ((ctorDeclaration.parent).declarations.length === 1) { - copyComments(precedingNode, newClassDeclaration!, sourceFile); // TODO: GH#18217 + copyLeadingComments(precedingNode, newClassDeclaration!, sourceFile); // TODO: GH#18217 changes.delete(sourceFile, precedingNode); } else { @@ -48,7 +48,7 @@ namespace ts.codefix { return undefined; } - copyComments(ctorDeclaration, newClassDeclaration, sourceFile); + copyLeadingComments(ctorDeclaration, newClassDeclaration, sourceFile); // Because the preceding node could be touched, we need to insert nodes before delete nodes. changes.insertNodeAfter(sourceFile, precedingNode!, newClassDeclaration); @@ -112,7 +112,7 @@ namespace ts.codefix { const fullModifiers = concatenate(modifiers, getModifierKindFromSource(functionExpression, SyntaxKind.AsyncKeyword)); const method = createMethod(/*decorators*/ undefined, fullModifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined, /*typeParameters*/ undefined, functionExpression.parameters, /*type*/ undefined, functionExpression.body); - copyComments(assignmentBinaryExpression, method, sourceFile); + copyLeadingComments(assignmentBinaryExpression, method, sourceFile); return method; } @@ -132,7 +132,7 @@ namespace ts.codefix { const fullModifiers = concatenate(modifiers, getModifierKindFromSource(arrowFunction, SyntaxKind.AsyncKeyword)); const method = createMethod(/*decorators*/ undefined, fullModifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined, /*typeParameters*/ undefined, arrowFunction.parameters, /*type*/ undefined, bodyBlock); - copyComments(assignmentBinaryExpression, method, sourceFile); + copyLeadingComments(assignmentBinaryExpression, method, sourceFile); return method; } @@ -143,7 +143,7 @@ namespace ts.codefix { } const prop = createProperty(/*decorators*/ undefined, modifiers, memberDeclaration.name, /*questionToken*/ undefined, /*type*/ undefined, assignmentBinaryExpression.right); - copyComments(assignmentBinaryExpression.parent, prop, sourceFile); + copyLeadingComments(assignmentBinaryExpression.parent, prop, sourceFile); return prop; } } diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index c5fccde13db..97d6f4d6667 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -48,13 +48,13 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { const returnStatement = createReturn(expression); body = createBlock([returnStatement], /* multiLine */ true); suppressLeadingAndTrailingTrivia(body); - copyComments(expression!, returnStatement, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ true); + copyLeadingComments(expression!, returnStatement, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ true); } else if (actionName === removeBracesActionName && returnStatement) { const actualExpression = expression || createVoidZero(); body = needsParentheses(actualExpression) ? createParen(actualExpression) : actualExpression; suppressLeadingAndTrailingTrivia(body); - copyComments(returnStatement, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false); + copyLeadingComments(returnStatement, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false); } else { Debug.fail("invalid action"); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 2b6d0acde88..46ec160c022 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1831,7 +1831,7 @@ namespace ts { return lastPos; } - export function copyComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) { + export function copyLeadingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) { forEachLeadingCommentRange(sourceFile.text, sourceNode.pos, (pos, end, kind, htnl) => { if (kind === SyntaxKind.MultiLineCommentTrivia) { // Remove leading /* @@ -1847,6 +1847,47 @@ namespace ts { }); } + export function copyTrailingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) { + forEachTrailingCommentRange(sourceFile.text, sourceNode.end, (pos, end, kind, htnl) => { + if (kind === SyntaxKind.MultiLineCommentTrivia) { + // Remove leading /* + pos += 2; + // Remove trailing */ + end -= 2; + } + else { + // Remove leading // + pos += 2; + } + addSyntheticTrailingComment(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl); + }); + } + + /** + * This function copies the trailing comments for the token that comes before `sourceNode`, as leading comments of `targetNode`. + * This is useful because sometimes a comment that refers to `sourceNode` will be a leading comment for `sourceNode`, according to the + * notion of trivia ownership, and instead will be a trailing comment for the token before `sourceNode`, e.g.: + * `function foo(\* not leading comment for a *\ a: string) {}` + * The comment refers to `a` but belongs to the `(` token, but we might want to copy it. + */ + export function copyTrailingAsLeadingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) { + forEachTrailingCommentRange(sourceFile.text, sourceNode.pos, (pos, end, kind, htnl) => { + if (kind === SyntaxKind.MultiLineCommentTrivia) { + // Remove leading /* + pos += 2; + // Remove trailing */ + end -= 2; + } + else { + // Remove leading // + pos += 2; + } + addSyntheticLeadingComment(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl); + }); + } + + + function indexInTextChange(change: string, name: string): number { if (startsWith(change, name)) return 0; // Add a " " to avoid references inside words From dba631de80784f99a5fd946460ca27a54ad18a01 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 11 Feb 2019 12:02:51 -0800 Subject: [PATCH 20/83] copy comments when refactoring --- .../refactors/convertToNamedParameters.ts | 83 +++++++++++++++++-- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 16716bed106..41c9935deb6 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -40,18 +40,26 @@ namespace ts.refactor.convertToNamedParameters { suppressLeadingAndTrailingTrivia(newFunctionDeclaration, /*recursive*/ false); changes.replaceNode(sourceFile, functionDeclaration, newFunctionDeclaration); - const nameNodes = getFunctionDeclarationNames(functionDeclaration); - const functionRefs = flatMap(nameNodes, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); + const functionNames = getFunctionDeclarationNames(functionDeclaration); + const functionRefs = flatMap(functionNames, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); const functionCalls = deduplicate(getDirectFunctionCalls(functionRefs), (a, b) => a === b); refactorSucceeded = true; // TODO: check if a bad reference was found forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { - const newArguments = getSynthesizedDeepClone(createNewArguments(functionDeclaration, call.arguments), /*includeTrivia*/ true); - changes.replaceNodeRange(getSourceFileOfNode(call), first(call.arguments), last(call.arguments), newArguments); + const newArgument = getSynthesizedDeepClone(createNewArguments(functionDeclaration, call.arguments), /*includeTrivia*/ true); + const newCall = updateCallArguments(call, createNodeArray([newArgument])); + suppressLeadingAndTrailingTrivia(newCall, /*recursive*/ false); + changes.replaceNode(getSourceFileOfNode(call), call, newCall); }}); } + function updateCallArguments(call: CallExpression | NewExpression, args: NodeArray) { + const newCall = getSynthesizedClone(call); + newCall.arguments = args; + return updateNode(newCall, call); + } + function updateDeclarationParameters(declaration: SignatureDeclaration, parameters: NodeArray): SignatureDeclaration { const newDeclaration = getSynthesizedClone(declaration); newDeclaration.parameters = parameters; @@ -160,7 +168,12 @@ namespace ts.refactor.convertToNamedParameters { const parameters = getRefactorableParameters(functionDeclaration.parameters); const hasRestParameter = isRestParameter(last(parameters)); const nonRestArguments = hasRestParameter ? args.slice(0, parameters.length - 1) : args; - const properties = map(nonRestArguments, (arg, i) => createPropertyAssignment(getParameterName(parameters[i]), arg)); + const properties = map(nonRestArguments, (arg, i) => { + const property = createPropertyAssignment(getParameterName(parameters[i]), arg); + suppressLeadingAndTrailingTrivia(property.initializer); + copyComments(arg, property.initializer); + return property; + }); if (hasRestParameter && args.length >= parameters.length) { const restArguments = args.slice(parameters.length - 1); @@ -168,7 +181,8 @@ namespace ts.refactor.convertToNamedParameters { properties.push(restProperty); } - return createObjectLiteral(properties, /*multiLine*/ false); + const objectLiteral = createObjectLiteral(properties, /*multiLine*/ false); + return objectLiteral; } function createNewParameters(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray { @@ -181,6 +195,12 @@ namespace ts.refactor.convertToNamedParameters { /*propertyName*/ undefined, getParameterName(paramDecl), isRestParameter(paramDecl) ? createArrayLiteral() : paramDecl.initializer); + + suppressLeadingAndTrailingTrivia(element); + if (paramDecl.initializer && element.initializer) { + copyComments(paramDecl.initializer, element.initializer); + } + return element; }); const paramName = createObjectBindingPattern(bindingElements); const paramType = createParamTypeNode(refactorableParameters); @@ -200,13 +220,29 @@ namespace ts.refactor.convertToNamedParameters { objectInitializer); if (hasThisParameter(functionDeclaration.parameters)) { - return createNodeArray([functionDeclaration.parameters[0], newParameter]); + const thisParam = functionDeclaration.parameters[0]; + const newThis = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + thisParam.name, + /*questionToken*/ undefined, + thisParam.type); + + suppressLeadingAndTrailingTrivia(newThis.name); + copyComments(thisParam.name, newThis.name); + if (thisParam.type && newThis.type) { + suppressLeadingAndTrailingTrivia(newThis.type); + copyComments(thisParam.type, newThis.type); + } + + return createNodeArray([newThis, newParameter]); } return createNodeArray([newParameter]); function createParamTypeNode(parameters: NodeArray): TypeLiteralNode { const members = map(parameters, createPropertySignatureFromParameterDeclaration); - const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); + const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); // TODO: add single line option to createTypeLiteralNode return typeNode; } @@ -216,12 +252,20 @@ namespace ts.refactor.convertToNamedParameters { paramType = getTypeNode(paramDeclaration); } - return createPropertySignature( + const propertySignature = createPropertySignature( /*modifiers*/ undefined, getParameterName(paramDeclaration), paramDeclaration.initializer || isRestParameter(paramDeclaration) ? createToken(SyntaxKind.QuestionToken) : paramDeclaration.questionToken, paramType, /*initializer*/ undefined); + + suppressLeadingAndTrailingTrivia(propertySignature); + copyComments(paramDeclaration.name, propertySignature.name); + if (paramDeclaration.type && propertySignature.type) { + copyComments(paramDeclaration.type, propertySignature.type); + } + + return propertySignature; } function getTypeNode(node: Node): TypeNode | undefined { @@ -231,6 +275,27 @@ namespace ts.refactor.convertToNamedParameters { } } + function copyComments(sourceNode: Node, targetNode: Node) { + const sourceFile = sourceNode.getSourceFile(); + const text = sourceFile.text; + if (hasLeadingLineBreak(sourceNode, text)) { + copyLeadingComments(sourceNode, targetNode, sourceFile); + } + else { + copyTrailingAsLeadingComments(sourceNode, targetNode, sourceFile); + } + copyTrailingComments(sourceNode, targetNode, sourceFile); + + function hasLeadingLineBreak(node: Node, text: string) { + const start = node.getFullStart(); + const end = node.getStart(); + for (let i = start; i < end; i++) { + if (text.charCodeAt(i) === CharacterCodes.lineFeed) return true; + } + return false; + } + } + function getParameterName(paramDeclaration: ValidParameterDeclaration) { return getTextOfIdentifierOrLiteral(paramDeclaration.name); } From 4e135f13b5efb89217a06a53cca5f81362630485 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 11 Feb 2019 12:03:09 -0800 Subject: [PATCH 21/83] add tests for comments --- .../refactorConvertToNamedParameters25.ts | 15 ++++++++++++ .../refactorConvertToNamedParameters26.ts | 23 +++++++++++++++++++ .../refactorConvertToNamedParameters27.ts | 17 ++++++++++++++ .../refactorConvertToNamedParameters28.ts | 12 ++++++++++ .../refactorConvertToNamedParameters29.ts | 13 +++++++++++ 5 files changed, 80 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters25.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters26.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters27.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters28.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters29.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters25.ts b/tests/cases/fourslash/refactorConvertToNamedParameters25.ts new file mode 100644 index 00000000000..1b51102f80e --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters25.ts @@ -0,0 +1,15 @@ +/// + +////function /*a*/foo/*b*/(a: number, b: number) { /** missing */ +//// return a + b; +////} + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function foo({ a, b }: { a: number; b: number; }) { /** missing */ + return a + b; +}` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters26.ts b/tests/cases/fourslash/refactorConvertToNamedParameters26.ts new file mode 100644 index 00000000000..20238ac81de --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters26.ts @@ -0,0 +1,23 @@ +/// + +////foo(1, 2); /**a*/ +/////**b*/ function /*a*/foo/*b*/(/**this1*/ this /**this2*/: /**void1*/ void /**void2*/, /**c*/ a /**d*/: /**e*/ number /**f*/, /**g*/ b /**h*/: /**i*/ number /**j*/ = /**k*/ 1 /**l*/) { +//// // m +//// /**n*/ return a + b; // o +//// // p +////} // q +/////**r*/ foo(1); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `foo({ a: 1, b: 2 }); /**a*/ +/**b*/ function foo(/**this1*/ this /**this2*/: /**void1*/ void /**void2*/, { a, b = /**k*/ 1 /**l*/ }: { /**c*/ a /**d*/: /**e*/ number /**f*/; /**g*/ b /**h*/?: /**i*/ number /**j*/; }) { + // m + /**n*/ return a + b; // o + // p +} // q +/**r*/ foo({ a: 1 });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters27.ts b/tests/cases/fourslash/refactorConvertToNamedParameters27.ts new file mode 100644 index 00000000000..5bcda77fe0b --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters27.ts @@ -0,0 +1,17 @@ +/// + +////function /*a*/foo/*b*/(a: number, b: number, ...rest: number[]) { +//// return a + b; +////} +////foo(/**a*/ 1 /**b*/, /**c*/ 2 /**d*/, /**e*/ 3 /**f*/, /**g*/ 4 /**h*/); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) { + return a + b; +} +foo({ a: /**a*/ 1 /**b*/, b: /**c*/ 2 /**d*/, rest: [/**e*/ 3 /**f*/, /**g*/ 4 /**h*/] });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters28.ts b/tests/cases/fourslash/refactorConvertToNamedParameters28.ts new file mode 100644 index 00000000000..8218ac65d80 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters28.ts @@ -0,0 +1,12 @@ +/// + +////function /*a*/foo/*b*/(// comment +//// /** other comment */ a: number, b: number) { } + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function foo(// comment { a, b }: { /** other comment */ a: number; b: number; }) { }` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters29.ts b/tests/cases/fourslash/refactorConvertToNamedParameters29.ts new file mode 100644 index 00000000000..02582785944 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters29.ts @@ -0,0 +1,13 @@ +/// + +////function /*a*/foo/*b*/(// comment +//// a: number, b: number) { } + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function foo(// comment + { a, b }: { a: number; b: number }) { }` +}); \ No newline at end of file From dbd84996aa749712fdd2ea8b4323be71d743c51d Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 11 Feb 2019 14:24:05 -0800 Subject: [PATCH 22/83] don't apply refactor when parameter has decorators --- src/services/refactors/convertToNamedParameters.ts | 3 ++- .../fourslash/refactorConvertToNamedParameters30.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters30.ts diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 41c9935deb6..bf11c5df8b7 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -142,7 +142,7 @@ namespace ts.refactor.convertToNamedParameters { } function isValidParameterDeclaration(paramDeclaration: ParameterDeclaration): paramDeclaration is ValidParameterDeclaration { - return !paramDeclaration.modifiers && isIdentifier(paramDeclaration.name); + return !paramDeclaration.modifiers && !paramDeclaration.decorators && isIdentifier(paramDeclaration.name); } } @@ -361,5 +361,6 @@ namespace ts.refactor.convertToNamedParameters { interface ValidParameterDeclaration extends ParameterDeclaration { name: Identifier; modifiers: undefined; + decorators: undefined; } } \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters30.ts b/tests/cases/fourslash/refactorConvertToNamedParameters30.ts new file mode 100644 index 00000000000..180798af95d --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters30.ts @@ -0,0 +1,11 @@ +/// + +////declare function required(target: Object, propertyKey: string | symbol, parameterIndex: number) +////class C { +//// /*a*/bar/*b*/(@required a: number, b: number) { +//// +//// } +////} + +goTo.select("a", "b"); +verify.not.refactorAvailable("Convert to named parameters"); \ No newline at end of file From ef4db31e84aa9dbf0a43cc7a1230f3aeb4797f6c Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 13 Feb 2019 11:35:44 -0800 Subject: [PATCH 23/83] don't apply changes when unexpected reference is found --- .../refactors/convertToNamedParameters.ts | 79 +++++++++++++++---- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index bf11c5df8b7..5207c9a1b45 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -11,8 +11,8 @@ namespace ts.refactor.convertToNamedParameters { function getAvailableActions(context: RefactorContext): ReadonlyArray { const { file, startPosition } = context; - const func = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker()); - if (!func) return emptyArray; + const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker()); + if (!functionDeclaration) return emptyArray; return [{ name: refactorName, @@ -31,7 +31,7 @@ namespace ts.refactor.convertToNamedParameters { if (!functionDeclaration || !cancellationToken) return undefined; const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, cancellationToken, host, t, functionDeclaration)); - return refactorSucceeded ? { renameFilename: undefined, renameLocation: undefined, edits } : undefined; + return { renameFilename: undefined, renameLocation: undefined, edits: refactorSucceeded ? edits : [] }; } function doChange(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration): void { @@ -42,8 +42,9 @@ namespace ts.refactor.convertToNamedParameters { const functionNames = getFunctionDeclarationNames(functionDeclaration); const functionRefs = flatMap(functionNames, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); - const functionCalls = deduplicate(getDirectFunctionCalls(functionRefs), (a, b) => a === b); - refactorSucceeded = true; // TODO: check if a bad reference was found + const groupedRefs = groupReferences(functionNames, functionRefs); + checkReferences(functionNames, groupedRefs); + const functionCalls = groupedRefs.calls; forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { @@ -66,9 +67,37 @@ namespace ts.refactor.convertToNamedParameters { return updateNode(newDeclaration, declaration); } - function getDirectFunctionCalls(referenceEntries: ReadonlyArray | undefined): ReadonlyArray { - return mapDefined(referenceEntries, (entry) => { - if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node.parent) { + function checkReferences(names: Node[], groupedRefs: GroupedReferences): void { + if (groupedRefs.unhandled.length > 0) { + refactorSucceeded = false; + } + if (groupedRefs.declarations.length > names.length) { + refactorSucceeded = false; + } + } + + function groupReferences(names: Node[], referenceEntries: ReadonlyArray | undefined): GroupedReferences { + const references: GroupedReferences = { calls: [], declarations: [], unhandled: [] }; + forEach(referenceEntries, (entry) => { + const decl = entryToDeclarationName(entry); + if (decl) { + references.declarations.push(decl); + return; + } + const call = entryToFunctionCall(entry); + if (call) { + references.calls.push(call); + return; + } + const node = entryToNode(entry); + if (node) { + references.unhandled.push(node); + } + }); + return references; + + function entryToFunctionCall(entry: FindAllReferences.Entry): CallExpression | NewExpression | undefined { + if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && entry.node.parent) { const functionRef = entry.node; const parent = functionRef.parent; switch (parent.kind) { @@ -109,7 +138,27 @@ namespace ts.refactor.convertToNamedParameters { } } return undefined; - }); + } + + function entryToDeclarationName(entry: FindAllReferences.Entry): Node | undefined { + if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && contains(names, entry.node)) { + return entry.node; + } + return undefined; + } + + function entryToNode(entry: FindAllReferences.Entry): Node | undefined { + if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node) { + return entry.node; + } + return undefined; + } + } + + interface GroupedReferences { + calls: (CallExpression | NewExpression)[]; + declarations: Node[]; + unhandled: Node[]; } function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined { @@ -126,7 +175,7 @@ namespace ts.refactor.convertToNamedParameters { return !!functionDeclaration.name && isPropertyName(functionDeclaration.name) && isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); case SyntaxKind.Constructor: if (isClassDeclaration(functionDeclaration.parent)) { - return !!functionDeclaration.parent.name && isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); + return isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); } else { return isVariableDeclaration(functionDeclaration.parent.parent) && !functionDeclaration.parent.parent.type && isVarConst(functionDeclaration.parent.parent) && isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); @@ -310,15 +359,13 @@ namespace ts.refactor.convertToNamedParameters { let name: Node; switch (functionDeclaration.parent.kind) { case SyntaxKind.ClassDeclaration: - name = functionDeclaration.parent.name; - break; + return [ctrKeyword!]; case SyntaxKind.ClassExpression: name = functionDeclaration.parent.parent.name; - break; + if (ctrKeyword) return [ctrKeyword, name]; + return [name]; default: return Debug.assertNever(functionDeclaration.parent); } - if (ctrKeyword) return [ctrKeyword, name]; - return [name]; case SyntaxKind.ArrowFunction: case SyntaxKind.FunctionExpression: return [functionDeclaration.parent.name]; @@ -330,7 +377,7 @@ namespace ts.refactor.convertToNamedParameters { type ValidVariableDeclaration = VariableDeclaration & { type: undefined }; interface ValidConstructor extends ConstructorDeclaration { - parent: (ClassDeclaration & { name: Identifier }) | (ClassExpression & { parent: ValidVariableDeclaration }); + parent: ClassDeclaration | (ClassExpression & { parent: ValidVariableDeclaration }); parameters: NodeArray; body: FunctionBody; } From ec0e734708fd5c601a06e456b9f680103b8c3eb5 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 13 Feb 2019 11:36:59 -0800 Subject: [PATCH 24/83] return function expression name in getFunctionDeclarationNames --- src/services/refactors/convertToNamedParameters.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 5207c9a1b45..d5e3a43be29 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -367,7 +367,9 @@ namespace ts.refactor.convertToNamedParameters { default: return Debug.assertNever(functionDeclaration.parent); } case SyntaxKind.ArrowFunction: + return [functionDeclaration.parent.name]; case SyntaxKind.FunctionExpression: + if (functionDeclaration.name) return [functionDeclaration.name, functionDeclaration.parent.name]; return [functionDeclaration.parent.name]; } } From 05e9d6c9ded138b48aeeeb03c3cf2d49c865b48d Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 13 Feb 2019 15:34:18 -0800 Subject: [PATCH 25/83] fix reference checking --- .../refactors/convertToNamedParameters.ts | 211 +++++++++--------- 1 file changed, 108 insertions(+), 103 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index d5e3a43be29..6e5881a31f6 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -5,7 +5,6 @@ namespace ts.refactor.convertToNamedParameters { const actionNameNamedParameters = "Convert to named parameters"; const actionDescriptionNamedParameters = "Convert to named parameters"; const minimumParameterLength = 1; - let refactorSucceeded = true; registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); @@ -30,22 +29,27 @@ namespace ts.refactor.convertToNamedParameters { const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker()); if (!functionDeclaration || !cancellationToken) return undefined; - const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, cancellationToken, host, t, functionDeclaration)); - return { renameFilename: undefined, renameLocation: undefined, edits: refactorSucceeded ? edits : [] }; + const functionNames = getFunctionDeclarationNames(functionDeclaration); + const groupedReferences = getGroupedReferences(functionNames, program, cancellationToken); + if (checkReferences(functionNames, groupedReferences)) { + const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, host, t, functionDeclaration, groupedReferences)); + return { renameFilename: undefined, renameLocation: undefined, edits }; + } + + return { edits: [] }; } - function doChange(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration): void { - const newParamDeclaration = map(createNewParameters(functionDeclaration, program, host), param => getSynthesizedDeepClone(param, /*includeTrivia*/ true)); - const newFunctionDeclaration = updateDeclarationParameters(functionDeclaration, createNodeArray(newParamDeclaration)); - suppressLeadingAndTrailingTrivia(newFunctionDeclaration, /*recursive*/ false); - changes.replaceNode(sourceFile, functionDeclaration, newFunctionDeclaration); - const functionNames = getFunctionDeclarationNames(functionDeclaration); - const functionRefs = flatMap(functionNames, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); - const groupedRefs = groupReferences(functionNames, functionRefs); - checkReferences(functionNames, groupedRefs); - const functionCalls = groupedRefs.calls; + function doChange(sourceFile: SourceFile, program: Program, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration, groupedReferences: GroupedReferences): void { + const newParamDeclaration = map(createNewParameters(functionDeclaration, program, host), param => getSynthesizedDeepClone(param)); + changes.replaceNodeRangeWithNodes( + sourceFile, + first(functionDeclaration.parameters), + last(functionDeclaration.parameters), + newParamDeclaration, + { joiner: ", ", indentation: 0 }); // indentation is set to 0 because otherwise the object parameter will be indented if there is a `this` parameter + const functionCalls = groupedReferences.calls; forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { const newArgument = getSynthesizedDeepClone(createNewArguments(functionDeclaration, call.arguments), /*includeTrivia*/ true); @@ -61,104 +65,99 @@ namespace ts.refactor.convertToNamedParameters { return updateNode(newCall, call); } - function updateDeclarationParameters(declaration: SignatureDeclaration, parameters: NodeArray): SignatureDeclaration { - const newDeclaration = getSynthesizedClone(declaration); - newDeclaration.parameters = parameters; - return updateNode(newDeclaration, declaration); - } + function getGroupedReferences(functionNames: Node[], program: Program, cancellationToken: CancellationToken): GroupedReferences { + const functionRefs = flatMap(functionNames, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); + const groupedReferences = groupReferences(functionRefs); + return groupedReferences; - function checkReferences(names: Node[], groupedRefs: GroupedReferences): void { - if (groupedRefs.unhandled.length > 0) { - refactorSucceeded = false; - } - if (groupedRefs.declarations.length > names.length) { - refactorSucceeded = false; - } - } - - function groupReferences(names: Node[], referenceEntries: ReadonlyArray | undefined): GroupedReferences { - const references: GroupedReferences = { calls: [], declarations: [], unhandled: [] }; - forEach(referenceEntries, (entry) => { - const decl = entryToDeclarationName(entry); - if (decl) { - references.declarations.push(decl); - return; - } - const call = entryToFunctionCall(entry); - if (call) { - references.calls.push(call); - return; - } - const node = entryToNode(entry); - if (node) { - references.unhandled.push(node); - } - }); - return references; - - function entryToFunctionCall(entry: FindAllReferences.Entry): CallExpression | NewExpression | undefined { - if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && entry.node.parent) { - const functionRef = entry.node; - const parent = functionRef.parent; - switch (parent.kind) { - // Function call (foo(...)) - case SyntaxKind.CallExpression: - const callExpression = tryCast(parent, isCallExpression); - if (callExpression && callExpression.expression === functionRef) { - return callExpression; - } - break; - // Constructor call (new Foo(...)) - case SyntaxKind.NewExpression: - const newExpression = tryCast(parent, isNewExpression); - if (newExpression && newExpression.expression === functionRef) { - return newExpression; - } - break; - // Method call (x.foo(...)) - case SyntaxKind.PropertyAccessExpression: - const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); - if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionRef) { - const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression); - if (callExpression && callExpression.expression === propertyAccessExpression) { - return callExpression; - } - } - break; - // Method call (x['foo'](...)) - case SyntaxKind.ElementAccessExpression: - const elementAccessExpression = tryCast(parent, isElementAccessExpression); - if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionRef) { - const callExpression = tryCast(elementAccessExpression.parent, isCallExpression); - if (callExpression && callExpression.expression === elementAccessExpression) { - return callExpression; - } - } - break; + function groupReferences(referenceEntries: ReadonlyArray | undefined): GroupedReferences { + const references: GroupedReferences = { calls: [], declarations: [], unhandled: [] }; + forEach(referenceEntries, (entry) => { + const decl = entryToDeclarationName(entry); + if (decl) { + references.declarations.push(decl); + return; } + const call = entryToFunctionCall(entry); + if (call) { + references.calls.push(call); + return; + } + const node = entryToNode(entry); + if (node) { + references.unhandled.push(node); + } + }); + return references; + + function entryToFunctionCall(entry: FindAllReferences.Entry): CallExpression | NewExpression | undefined { + if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && entry.node.parent) { + const functionRef = entry.node; + const parent = functionRef.parent; + switch (parent.kind) { + // Function call (foo(...)) + case SyntaxKind.CallExpression: + const callExpression = tryCast(parent, isCallExpression); + if (callExpression && callExpression.expression === functionRef) { + return callExpression; + } + break; + // Constructor call (new Foo(...)) + case SyntaxKind.NewExpression: + const newExpression = tryCast(parent, isNewExpression); + if (newExpression && newExpression.expression === functionRef) { + return newExpression; + } + break; + // Method call (x.foo(...)) + case SyntaxKind.PropertyAccessExpression: + const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); + if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionRef) { + const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression); + if (callExpression && callExpression.expression === propertyAccessExpression) { + return callExpression; + } + } + break; + // Method call (x['foo'](...)) + case SyntaxKind.ElementAccessExpression: + const elementAccessExpression = tryCast(parent, isElementAccessExpression); + if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionRef) { + const callExpression = tryCast(elementAccessExpression.parent, isCallExpression); + if (callExpression && callExpression.expression === elementAccessExpression) { + return callExpression; + } + } + break; + } + } + return undefined; } - return undefined; - } - - function entryToDeclarationName(entry: FindAllReferences.Entry): Node | undefined { - if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && contains(names, entry.node)) { - return entry.node; + + function entryToDeclarationName(entry: FindAllReferences.Entry): Node | undefined { + if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && contains(functionNames, entry.node)) { + return entry.node; + } + return undefined; } - return undefined; - } - - function entryToNode(entry: FindAllReferences.Entry): Node | undefined { - if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node) { - return entry.node; + + function entryToNode(entry: FindAllReferences.Entry): Node | undefined { + if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node) { + return entry.node; + } + return undefined; } - return undefined; } } - interface GroupedReferences { - calls: (CallExpression | NewExpression)[]; - declarations: Node[]; - unhandled: Node[]; + function checkReferences(functionNames: Node[], groupedReferences: GroupedReferences): boolean { + if (groupedReferences.unhandled.length > 0) { + return false; + } + if (groupedReferences.declarations.length > functionNames.length) { + return false; + } + return true; } function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined { @@ -412,4 +411,10 @@ namespace ts.refactor.convertToNamedParameters { modifiers: undefined; decorators: undefined; } + + interface GroupedReferences { + calls: (CallExpression | NewExpression)[]; + declarations: Node[]; + unhandled: Node[]; + } } \ No newline at end of file From b93afffaf7cbb80749c9baefedeedca1610cf9ea Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 15 Feb 2019 15:36:11 -0800 Subject: [PATCH 26/83] rename refactor tests --- .../refactorConvertToNamedParameters10.ts | 7 --- .../refactorConvertToNamedParameters19.ts | 24 ---------- .../refactorConvertToNamedParameters28.ts | 12 ----- .../refactorConvertToNamedParameters8.ts | 17 ------- ...ertToNamedParameters_allParamsOptional.ts} | 0 ...ConvertToNamedParameters_arrowFunction.ts} | 0 ...meters_arrowFunctionWithContextualType.ts} | 0 ...rConvertToNamedParameters_callComments.ts} | 0 ...rConvertToNamedParameters_callComments2.ts | 25 +++++++++++ ...orConvertToNamedParameters_chainedCall.ts} | 0 ...nvertToNamedParameters_classExpression.ts} | 0 ...tToNamedParameters_classTypeParameters.ts} | 0 ...orConvertToNamedParameters_constructor.ts} | 0 ...rConvertToNamedParameters_defaultClass.ts} | 0 ...actorConvertToNamedParameters_function.ts} | 0 ...vertToNamedParameters_functionComments.ts} | 0 ...ertToNamedParameters_functionComments1.ts} | 4 +- ...ertToNamedParameters_functionComments2.ts} | 17 ++++++- ...rtToNamedParameters_functionExpression.ts} | 0 ...NamedParameters_functionTypeParameters.ts} | 2 +- ...orConvertToNamedParameters_initializer.ts} | 0 ...ToNamedParameters_initializerInference.ts} | 0 ...efactorConvertToNamedParameters_method.ts} | 0 ...orConvertToNamedParameters_methodCalls.ts} | 0 ...onvertToNamedParameters_methodOverrides.ts | 45 +++++++++++++++++++ ...ctorConvertToNamedParameters_overloads.ts} | 0 ...onvertToNamedParameters_paramDecorator.ts} | 0 ...vertToNamedParameters_recursiveFunction.ts | 20 +++++++++ ...rtToNamedParameters_restParamInference.ts} | 0 ...rConvertToNamedParameters_staticMethod.ts} | 0 ...ctorConvertToNamedParameters_superCall.ts} | 0 ...ctorConvertToNamedParameters_thisParam.ts} | 2 +- ...onvertToNamedParameters_typedRestParam.ts} | 0 ...vertToNamedParameters_varArrowFunction.ts} | 0 34 files changed, 109 insertions(+), 66 deletions(-) delete mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters10.ts delete mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters19.ts delete mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters28.ts delete mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters8.ts rename tests/cases/fourslash/{refactorConvertToNamedParameters7.ts => refactorConvertToNamedParameters_allParamsOptional.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters12.ts => refactorConvertToNamedParameters_arrowFunction.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters22.ts => refactorConvertToNamedParameters_arrowFunctionWithContextualType.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters27.ts => refactorConvertToNamedParameters_callComments.ts} (100%) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts rename tests/cases/fourslash/{refactorConvertToNamedParameters20.ts => refactorConvertToNamedParameters_chainedCall.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters14.ts => refactorConvertToNamedParameters_classExpression.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters16.ts => refactorConvertToNamedParameters_classTypeParameters.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters2.ts => refactorConvertToNamedParameters_constructor.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters15.ts => refactorConvertToNamedParameters_defaultClass.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters.ts => refactorConvertToNamedParameters_function.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters26.ts => refactorConvertToNamedParameters_functionComments.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters25.ts => refactorConvertToNamedParameters_functionComments1.ts} (62%) rename tests/cases/fourslash/{refactorConvertToNamedParameters29.ts => refactorConvertToNamedParameters_functionComments2.ts} (56%) rename tests/cases/fourslash/{refactorConvertToNamedParameters11.ts => refactorConvertToNamedParameters_functionExpression.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters17.ts => refactorConvertToNamedParameters_functionTypeParameters.ts} (90%) rename tests/cases/fourslash/{refactorConvertToNamedParameters6.ts => refactorConvertToNamedParameters_initializer.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters4.ts => refactorConvertToNamedParameters_initializerInference.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters1.ts => refactorConvertToNamedParameters_method.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters5.ts => refactorConvertToNamedParameters_methodCalls.ts} (100%) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_methodOverrides.ts rename tests/cases/fourslash/{refactorConvertToNamedParameters9.ts => refactorConvertToNamedParameters_overloads.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters30.ts => refactorConvertToNamedParameters_paramDecorator.ts} (100%) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_recursiveFunction.ts rename tests/cases/fourslash/{refactorConvertToNamedParameters23.ts => refactorConvertToNamedParameters_restParamInference.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters3.ts => refactorConvertToNamedParameters_staticMethod.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters21.ts => refactorConvertToNamedParameters_superCall.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters18.ts => refactorConvertToNamedParameters_thisParam.ts} (91%) rename tests/cases/fourslash/{refactorConvertToNamedParameters24.ts => refactorConvertToNamedParameters_typedRestParam.ts} (100%) rename tests/cases/fourslash/{refactorConvertToNamedParameters13.ts => refactorConvertToNamedParameters_varArrowFunction.ts} (100%) diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters10.ts b/tests/cases/fourslash/refactorConvertToNamedParameters10.ts deleted file mode 100644 index 209a06d5bfa..00000000000 --- a/tests/cases/fourslash/refactorConvertToNamedParameters10.ts +++ /dev/null @@ -1,7 +0,0 @@ -/// - -////const { foo, bar } = { foo: /*a*/(a: number, b: number)/*b*/ => {}, bar: () => {} }; -////foo(1, 2); - -goTo.select("a", "b"); -verify.not.refactorAvailable("Convert to named parameters"); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters19.ts b/tests/cases/fourslash/refactorConvertToNamedParameters19.ts deleted file mode 100644 index b635ce5e379..00000000000 --- a/tests/cases/fourslash/refactorConvertToNamedParameters19.ts +++ /dev/null @@ -1,24 +0,0 @@ -/// - -////class Foo { -//// /*a*/bar/*b*/(t: string, s: string): string { -//// return s + t; -//// } -////} -////var foo = {}; -////foo['bar']("a", "b"); -/// - -goTo.select("a", "b"); -edit.applyRefactor({ - refactorName: "Convert to named parameters", - actionName: "Convert to named parameters", - actionDescription: "Convert to named parameters", - newContent: `class Foo { - bar({ t, s }: { t: string; s: string; }): string { - return s + t; - } -} -var foo = {}; -foo['bar']("a", "b");` -}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters28.ts b/tests/cases/fourslash/refactorConvertToNamedParameters28.ts deleted file mode 100644 index 8218ac65d80..00000000000 --- a/tests/cases/fourslash/refactorConvertToNamedParameters28.ts +++ /dev/null @@ -1,12 +0,0 @@ -/// - -////function /*a*/foo/*b*/(// comment -//// /** other comment */ a: number, b: number) { } - -goTo.select("a", "b"); -edit.applyRefactor({ - refactorName: "Convert to named parameters", - actionName: "Convert to named parameters", - actionDescription: "Convert to named parameters", - newContent: `function foo(// comment { a, b }: { /** other comment */ a: number; b: number; }) { }` -}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters8.ts b/tests/cases/fourslash/refactorConvertToNamedParameters8.ts deleted file mode 100644 index 2c3d7de339f..00000000000 --- a/tests/cases/fourslash/refactorConvertToNamedParameters8.ts +++ /dev/null @@ -1,17 +0,0 @@ -/// - -////function f(/*a*/a: number, b = 1/*b*/) { -//// return b; -////} -////f(2); - -goTo.select("a", "b"); -edit.applyRefactor({ - refactorName: "Convert to named parameters", - actionName: "Convert to named parameters", - actionDescription: "Convert to named parameters", - newContent: `function f({ a, b = 1 }: { a: number; b?: number; }) { - return b; -} -f({ a: 2 });` -}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters7.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_allParamsOptional.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters7.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_allParamsOptional.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters12.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_arrowFunction.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters12.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_arrowFunction.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters22.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_arrowFunctionWithContextualType.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters22.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_arrowFunctionWithContextualType.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters27.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters27.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_callComments.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts new file mode 100644 index 00000000000..e0a78b091fc --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts @@ -0,0 +1,25 @@ +/// + +////function /*a*/foo/*b*/(a: number, b: number, ...rest: number[]) { +//// return a + b; +////} +////foo( +//// /**a*/ +//// 1, +//// /**c*/ +//// 2, +//// /**e*/ +//// 3, +//// /**g*/ +//// 4); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) { + return a + b; +} +foo({ a: /**a*/ 1, b: /**c*/ 2, rest: [/**e*/ 3, /**g*/ 4] });` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters20.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_chainedCall.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters20.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_chainedCall.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters14.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_classExpression.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters14.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_classExpression.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters16.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_classTypeParameters.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters16.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_classTypeParameters.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters2.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_constructor.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters2.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_constructor.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters15.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_defaultClass.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters15.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_defaultClass.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_function.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_function.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters26.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_functionComments.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters26.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_functionComments.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters25.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_functionComments1.ts similarity index 62% rename from tests/cases/fourslash/refactorConvertToNamedParameters25.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_functionComments1.ts index 1b51102f80e..e0d34c8fe65 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters25.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_functionComments1.ts @@ -1,6 +1,6 @@ /// -////function /*a*/foo/*b*/(a: number, b: number) { /** missing */ +////function /*a*/foo/*b*/(a: number /** a */, b: number /** b */) { //// return a + b; ////} @@ -9,7 +9,7 @@ edit.applyRefactor({ refactorName: "Convert to named parameters", actionName: "Convert to named parameters", actionDescription: "Convert to named parameters", - newContent: `function foo({ a, b }: { a: number; b: number; }) { /** missing */ + newContent: `function foo({ a, b }: { a: number /** a */; b: number /** b */; }) { return a + b; }` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters29.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_functionComments2.ts similarity index 56% rename from tests/cases/fourslash/refactorConvertToNamedParameters29.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_functionComments2.ts index 02582785944..923b49b4198 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters29.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_functionComments2.ts @@ -1,7 +1,13 @@ /// ////function /*a*/foo/*b*/(// comment -//// a: number, b: number) { } +//// // a comment +//// a: number, +//// // b comment +//// b: number +////) { +//// return a + b; +////} goTo.select("a", "b"); edit.applyRefactor({ @@ -9,5 +15,12 @@ edit.applyRefactor({ actionName: "Convert to named parameters", actionDescription: "Convert to named parameters", newContent: `function foo(// comment - { a, b }: { a: number; b: number }) { }` +{ a, b }: { + // a comment + a: number; + // b comment + b: number; +}) { + return a + b; +}` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters11.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_functionExpression.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters11.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_functionExpression.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters17.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_functionTypeParameters.ts similarity index 90% rename from tests/cases/fourslash/refactorConvertToNamedParameters17.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_functionTypeParameters.ts index 7888c33f4e2..a1597fdeed1 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters17.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_functionTypeParameters.ts @@ -1,7 +1,7 @@ /// ////function foo(/*a*/t: T, s: S/*b*/) { -//// return s; +//// return s; ////} ////foo("a", "b"); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters6.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_initializer.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters6.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_initializer.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters4.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_initializerInference.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters4.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_initializerInference.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters1.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_method.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters1.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_method.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters5.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_methodCalls.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters5.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_methodCalls.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_methodOverrides.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_methodOverrides.ts new file mode 100644 index 00000000000..b6daac17dd4 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_methodOverrides.ts @@ -0,0 +1,45 @@ +/// + +////class A { +//// /*a*/foo/*b*/(a: number, b: number) { } +////} +////class B extends A { +//// /*c*/foo/*d*/(c: number, d: number) { } +////} +////var a = new A(); +////a.foo(3, 4); +////var b = new B(); +////b.foo(5, 6); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class A { + foo(a: number, b: number) { } +} +class B extends A { + foo(c: number, d: number) { } +} +var a = new A(); +a.foo(3, 4); +var b = new B(); +b.foo(5, 6);` +}); +goTo.select("c", "d"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class A { + foo(a: number, b: number) { } +} +class B extends A { + foo(c: number, d: number) { } +} +var a = new A(); +a.foo(3, 4); +var b = new B(); +b.foo(5, 6);` +}); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters9.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_overloads.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters9.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_overloads.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters30.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_paramDecorator.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters30.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_paramDecorator.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_recursiveFunction.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_recursiveFunction.ts new file mode 100644 index 00000000000..d1513cb7bf0 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_recursiveFunction.ts @@ -0,0 +1,20 @@ +/// + +////const f = function foo(/*a*/a: number, b: number/*b*/) { +//// foo(1, 2); +////} +////function foo(a: number, b: number) { } +////foo(3, 4); + + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `const f = function foo({ a, b }: { a: number; b: number; }) { + foo({ a: 1, b: 2 }); +} +function foo(a: number, b: number) { } +foo(3, 4);` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters23.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_restParamInference.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters23.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_restParamInference.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters3.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_staticMethod.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters3.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_staticMethod.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters21.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_superCall.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters21.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_superCall.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters18.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_thisParam.ts similarity index 91% rename from tests/cases/fourslash/refactorConvertToNamedParameters18.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_thisParam.ts index 069182e20e5..e4a96bc7bf1 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters18.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_thisParam.ts @@ -1,7 +1,7 @@ /// ////function foo(this: void, /*a*/t: string, s: string/*b*/) { -//// return s; +//// return s; ////} ////foo("a", "b"); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters24.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_typedRestParam.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters24.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_typedRestParam.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters13.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_varArrowFunction.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertToNamedParameters13.ts rename to tests/cases/fourslash/refactorConvertToNamedParameters_varArrowFunction.ts From eef3da5b6bf9372ea2899e0cd4fadcb33e0948cd Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 15 Feb 2019 15:38:10 -0800 Subject: [PATCH 27/83] create new ConfigurableStart and ConfigurableEnd options and rename them --- src/services/organizeImports.ts | 4 +- .../refactors/convertToNamedParameters.ts | 8 ++- src/services/textChanges.ts | 69 ++++++++++++------- .../unittests/services/textChanges.ts | 26 +++---- 4 files changed, 65 insertions(+), 42 deletions(-) diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index 157ba98e262..8096ffa4ad2 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -68,8 +68,8 @@ namespace ts.OrganizeImports { else { // Note: Delete the surrounding trivia because it will have been retained in newImportDecls. changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, { - useNonAdjustedStartPosition: true, // Leave header comment in place - useNonAdjustedEndPosition: false, + startPosition: textChanges.LeadingTriviaOption.Exclude, // Leave header comment in place + endPosition: textChanges.TrailingTriviaOption.Include, suffix: getNewLineOrDefaultFromHost(host, formatContext.options), }); } diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 6e5881a31f6..cf348bd9e73 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -47,7 +47,13 @@ namespace ts.refactor.convertToNamedParameters { first(functionDeclaration.parameters), last(functionDeclaration.parameters), newParamDeclaration, - { joiner: ", ", indentation: 0 }); // indentation is set to 0 because otherwise the object parameter will be indented if there is a `this` parameter + { joiner: ", ", + // indentation is set to 0 because otherwise the object parameter will be indented if there is a `this` parameter + indentation: 0, + startPosition: textChanges.LeadingTriviaOption.IncludeAll, + endPosition: textChanges.TrailingTriviaOption.Include + }); + const functionCalls = groupedReferences.calls; forEach(functionCalls, call => { diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 4689e18508c..546fddaca14 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -28,17 +28,32 @@ namespace ts.textChanges { } export interface ConfigurableStart { - /** True to use getStart() (NB, not getFullStart()) without adjustment. */ - useNonAdjustedStartPosition?: boolean; + startPosition?: LeadingTriviaOption; } export interface ConfigurableEnd { /** True to use getEnd() without adjustment. */ - useNonAdjustedEndPosition?: boolean; + endPosition?: TrailingTriviaOption; } - export enum Position { - FullStart, - Start + export enum LeadingTriviaOption { + /** Exclude all leading trivia (use getStart()) */ + Exclude, + /** Include leading trivia (default behavior) */ + Include, + /** Include leading trivia and, + * if there are no line breaks between the node and the previous token, + * include all trivia between the node and the previous token + */ + IncludeAll, + } + + export enum TrailingTriviaOption { + /** Exclude all leading trivia (use getEnd()) */ + Exclude, + /** TODO (default behavior) */ + IncludeIfLineBreak, + /** Include trailing trivia */ + Include, } function skipWhitespacesAndLineBreaks(text: string, start: number) { @@ -73,8 +88,8 @@ namespace ts.textChanges { export interface ConfigurableStartEnd extends ConfigurableStart, ConfigurableEnd {} export const useNonAdjustedPositions: ConfigurableStartEnd = { - useNonAdjustedStartPosition: true, - useNonAdjustedEndPosition: true, + startPosition: LeadingTriviaOption.Exclude, + endPosition: TrailingTriviaOption.Exclude, }; export interface InsertNodeOptions { @@ -143,11 +158,12 @@ namespace ts.textChanges { } function getAdjustedRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd): TextRange { - return { pos: getAdjustedStartPosition(sourceFile, startNode, options, Position.Start), end: getAdjustedEndPosition(sourceFile, endNode, options) }; + return { pos: getAdjustedStartPosition(sourceFile, startNode, options), end: getAdjustedEndPosition(sourceFile, endNode, options) }; } - function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStart, position: Position) { - if (options.useNonAdjustedStartPosition) { + function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStart) { + const { startPosition } = options; + if (startPosition === LeadingTriviaOption.Exclude) { return node.getStart(sourceFile); } const fullStart = node.getFullStart(); @@ -165,7 +181,7 @@ namespace ts.textChanges { // fullstart // when b is replaced - we usually want to keep the leading trvia // when b is deleted - we delete it - return position === Position.Start ? start : fullStart; + return startPosition === LeadingTriviaOption.IncludeAll ? fullStart : start; } // get start position of the line following the line that contains fullstart position // (but only if the fullstart isn't the very beginning of the file) @@ -178,11 +194,12 @@ namespace ts.textChanges { function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) { const { end } = node; - if (options.useNonAdjustedEndPosition || isExpression(node)) { + const { endPosition } = options; + if (endPosition === TrailingTriviaOption.Exclude || isExpression(node)) { return end; } const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true); - return newEnd !== end && isLineBreak(sourceFile.text.charCodeAt(newEnd - 1)) + return newEnd !== end && (endPosition === TrailingTriviaOption.Include || isLineBreak(sourceFile.text.charCodeAt(newEnd - 1))) ? newEnd : end; } @@ -240,15 +257,15 @@ namespace ts.textChanges { this.deleteRange(sourceFile, { pos: modifier.getStart(sourceFile), end: skipTrivia(sourceFile.text, modifier.end, /*stopAfterLineBreak*/ true) }); } - public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = {}): void { - const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.FullStart); + public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = { startPosition: LeadingTriviaOption.IncludeAll }): void { + const startPosition = getAdjustedStartPosition(sourceFile, startNode, options); const endPosition = getAdjustedEndPosition(sourceFile, endNode, options); this.deleteRange(sourceFile, { pos: startPosition, end: endPosition }); } - public deleteNodeRangeExcludingEnd(sourceFile: SourceFile, startNode: Node, afterEndNode: Node | undefined, options: ConfigurableStartEnd = {}): void { - const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.FullStart); - const endPosition = afterEndNode === undefined ? sourceFile.text.length : getAdjustedStartPosition(sourceFile, afterEndNode, options, Position.FullStart); + public deleteNodeRangeExcludingEnd(sourceFile: SourceFile, startNode: Node, afterEndNode: Node | undefined, options: ConfigurableStartEnd = { startPosition: LeadingTriviaOption.IncludeAll }): void { + const startPosition = getAdjustedStartPosition(sourceFile, startNode, options); + const endPosition = afterEndNode === undefined ? sourceFile.text.length : getAdjustedStartPosition(sourceFile, afterEndNode, options); this.deleteRange(sourceFile, { pos: startPosition, end: endPosition }); } @@ -307,7 +324,7 @@ namespace ts.textChanges { } public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, blankLineBetween = false): void { - this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, {}, Position.Start), newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween)); + this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, {}), newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween)); } public insertModifierBefore(sourceFile: SourceFile, modifier: SyntaxKind, before: Node): void { @@ -427,7 +444,7 @@ namespace ts.textChanges { } public insertNodeAtEndOfScope(sourceFile: SourceFile, scope: Node, newNode: Node): void { - const pos = getAdjustedStartPosition(sourceFile, scope.getLastToken()!, {}, Position.Start); + const pos = getAdjustedStartPosition(sourceFile, scope.getLastToken()!, {}); this.insertNodeAt(sourceFile, pos, newNode, { prefix: isLineBreak(sourceFile.text.charCodeAt(scope.getLastToken()!.pos)) ? this.newLineCharacter : this.newLineCharacter + this.newLineCharacter, suffix: this.newLineCharacter @@ -736,7 +753,7 @@ namespace ts.textChanges { // find first non-whitespace position in the leading trivia of the node function startPositionToDeleteNodeInList(sourceFile: SourceFile, node: Node): number { - return skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, {}, Position.FullStart), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); + return skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, { startPosition: LeadingTriviaOption.IncludeAll }), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); } function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, sourceFile: SourceFile): [number, number] { @@ -1090,7 +1107,7 @@ namespace ts.textChanges { case SyntaxKind.ImportDeclaration: deleteNode(changes, sourceFile, node, // For first import, leave header comment in place - node === sourceFile.imports[0].parent ? { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: false } : undefined); + node === sourceFile.imports[0].parent ? { startPosition: LeadingTriviaOption.Exclude, endPosition: TrailingTriviaOption.IncludeIfLineBreak } : undefined); break; case SyntaxKind.BindingElement: @@ -1134,7 +1151,7 @@ namespace ts.textChanges { deleteNodeInList(changes, deletedNodesInLists, sourceFile, node); } else { - deleteNode(changes, sourceFile, node, node.kind === SyntaxKind.SemicolonToken ? { useNonAdjustedEndPosition: true } : undefined); + deleteNode(changes, sourceFile, node, node.kind === SyntaxKind.SemicolonToken ? { endPosition: TrailingTriviaOption.Exclude } : undefined); } } } @@ -1213,8 +1230,8 @@ namespace ts.textChanges { /** Warning: This deletes comments too. See `copyComments` in `convertFunctionToEs6Class`. */ // Exported for tests only! (TODO: improve tests to not need this) - export function deleteNode(changes: ChangeTracker, sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = {}): void { - const startPosition = getAdjustedStartPosition(sourceFile, node, options, Position.FullStart); + export function deleteNode(changes: ChangeTracker, sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = { startPosition: LeadingTriviaOption.IncludeAll }): void { + const startPosition = getAdjustedStartPosition(sourceFile, node, options); const endPosition = getAdjustedEndPosition(sourceFile, node, options); changes.deleteRange(sourceFile, { pos: startPosition, end: endPosition }); } diff --git a/src/testRunner/unittests/services/textChanges.ts b/src/testRunner/unittests/services/textChanges.ts index de92efafbeb..f9e03a578de 100644 --- a/src/testRunner/unittests/services/textChanges.ts +++ b/src/testRunner/unittests/services/textChanges.ts @@ -140,13 +140,13 @@ var z = 3; // comment 4 deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile)); }); runSingleFileTest("deleteNode2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedStartPosition: true }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { startPosition: textChanges.LeadingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedEndPosition: true }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { endPosition: textChanges.TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode5", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { deleteNode(changeTracker, sourceFile, findVariableStatementContaining("x", sourceFile)); @@ -167,15 +167,15 @@ var a = 4; // comment 7 }); runSingleFileTest("deleteNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { useNonAdjustedStartPosition: true }); + { startPosition: textChanges.LeadingTriviaOption.Exclude }); }); runSingleFileTest("deleteNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { useNonAdjustedEndPosition: true }); + { endPosition: textChanges.TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true }); + { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); }); } function createTestVariableDeclaration(name: string) { @@ -254,16 +254,16 @@ var a = 4; // comment 7`; changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter }); }); runSingleFileTest("replaceNode2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, suffix: newLineCharacter, prefix: newLineCharacter }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); }); runSingleFileTest("replaceNode3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedEndPosition: true, suffix: newLineCharacter }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { endPosition: textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter }); }); runSingleFileTest("replaceNode4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); }); runSingleFileTest("replaceNode5", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); }); } { @@ -279,13 +279,13 @@ var a = 4; // comment 7`; changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { suffix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, suffix: newLineCharacter, prefix: newLineCharacter }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedEndPosition: true, suffix: newLineCharacter }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { endPosition: textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); }); } { From b6c8382c78596f1b90c6b89dc6368c163b6fa9f6 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 15 Feb 2019 15:51:20 -0800 Subject: [PATCH 28/83] replace argument list instead of whole call when refactoring --- .../refactors/convertToNamedParameters.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index cf348bd9e73..dc8043ddfa9 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -59,18 +59,15 @@ namespace ts.refactor.convertToNamedParameters { forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { const newArgument = getSynthesizedDeepClone(createNewArguments(functionDeclaration, call.arguments), /*includeTrivia*/ true); - const newCall = updateCallArguments(call, createNodeArray([newArgument])); - suppressLeadingAndTrailingTrivia(newCall, /*recursive*/ false); - changes.replaceNode(getSourceFileOfNode(call), call, newCall); + changes.replaceNodeRange( + getSourceFileOfNode(call), + first(call.arguments), + last(call.arguments), + newArgument, + { startPosition: textChanges.LeadingTriviaOption.IncludeAll, endPosition: textChanges.TrailingTriviaOption.Include }); }}); } - function updateCallArguments(call: CallExpression | NewExpression, args: NodeArray) { - const newCall = getSynthesizedClone(call); - newCall.arguments = args; - return updateNode(newCall, call); - } - function getGroupedReferences(functionNames: Node[], program: Program, cancellationToken: CancellationToken): GroupedReferences { const functionRefs = flatMap(functionNames, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); const groupedReferences = groupReferences(functionRefs); From a7730461816c5423b66b27b074a5983402322442 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 15 Feb 2019 16:48:54 -0800 Subject: [PATCH 29/83] copy argument comments to property --- src/services/refactors/convertToNamedParameters.ts | 6 +++--- .../refactorConvertToNamedParameters_callComments.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index dc8043ddfa9..8b966ad0ab2 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -58,7 +58,7 @@ namespace ts.refactor.convertToNamedParameters { const functionCalls = groupedReferences.calls; forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { - const newArgument = getSynthesizedDeepClone(createNewArguments(functionDeclaration, call.arguments), /*includeTrivia*/ true); + const newArgument = getSynthesizedDeepClone(createNewArgument(functionDeclaration, call.arguments), /*includeTrivia*/ true); changes.replaceNodeRange( getSourceFileOfNode(call), first(call.arguments), @@ -215,14 +215,14 @@ namespace ts.refactor.convertToNamedParameters { return parameters; } - function createNewArguments(functionDeclaration: ValidFunctionDeclaration, args: NodeArray): ObjectLiteralExpression { + function createNewArgument(functionDeclaration: ValidFunctionDeclaration, args: NodeArray): ObjectLiteralExpression { const parameters = getRefactorableParameters(functionDeclaration.parameters); const hasRestParameter = isRestParameter(last(parameters)); const nonRestArguments = hasRestParameter ? args.slice(0, parameters.length - 1) : args; const properties = map(nonRestArguments, (arg, i) => { const property = createPropertyAssignment(getParameterName(parameters[i]), arg); suppressLeadingAndTrailingTrivia(property.initializer); - copyComments(arg, property.initializer); + copyComments(arg, property); return property; }); diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_callComments.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments.ts index 5bcda77fe0b..6fb06fe938b 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters_callComments.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments.ts @@ -13,5 +13,5 @@ edit.applyRefactor({ newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) { return a + b; } -foo({ a: /**a*/ 1 /**b*/, b: /**c*/ 2 /**d*/, rest: [/**e*/ 3 /**f*/, /**g*/ 4 /**h*/] });` +foo({ /**a*/ a: 1 /**b*/, /**c*/ b: 2 /**d*/, rest: [/**e*/ 3 /**f*/, /**g*/ 4 /**h*/] });` }); \ No newline at end of file From 78968b12813e096d6da0ac65df01e6cbccfb1f69 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 19 Feb 2019 09:10:27 -0800 Subject: [PATCH 30/83] don't provide refactor in js file --- src/services/refactors/convertToNamedParameters.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 8b966ad0ab2..9c88e5ad337 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -10,6 +10,8 @@ namespace ts.refactor.convertToNamedParameters { function getAvailableActions(context: RefactorContext): ReadonlyArray { const { file, startPosition } = context; + const isJSFile = isSourceFileJS(file); + if (isJSFile) return emptyArray; const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker()); if (!functionDeclaration) return emptyArray; From 17b89653c228358b1fcca8ab6089396f32aff999 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 19 Feb 2019 14:20:37 -0800 Subject: [PATCH 31/83] include trailing trivia after expression in getAdjustedEndPosition if endPosition is include --- src/services/textChanges.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 546fddaca14..b8e8ee90ee9 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -195,7 +195,7 @@ namespace ts.textChanges { function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) { const { end } = node; const { endPosition } = options; - if (endPosition === TrailingTriviaOption.Exclude || isExpression(node)) { + if (endPosition === TrailingTriviaOption.Exclude || (isExpression(node) && endPosition !== TrailingTriviaOption.Include)) { return end; } const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true); From edf0cec3ddd7f0f8c7a1b58501668e6b9c70ddd0 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 19 Feb 2019 14:22:59 -0800 Subject: [PATCH 32/83] add tests for inherited constructor and method --- ...tToNamedParameters_inheritedConstructor.ts | 21 ++++++++++++++++ ...onvertToNamedParameters_inheritedMethod.ts | 25 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_inheritedConstructor.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_inheritedMethod.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedConstructor.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedConstructor.ts new file mode 100644 index 00000000000..743fc7fadb4 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedConstructor.ts @@ -0,0 +1,21 @@ +/// + +////class Foo { +//// /*a*/constructor/*b*/(t: string, s: string) { } +////} +////class Bar extends Foo { } +////var bar = new Bar("a", "b"); +////var foo = new Foo("c", "d"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + constructor({ t, s }: { t: string; s: string; }) { } +} +class Bar extends Foo { } +var bar = new Bar({ t: "a", s: "b" }); +var foo = new Foo({ t: "c", s: "d" })` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedMethod.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedMethod.ts new file mode 100644 index 00000000000..3214ea4bf53 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedMethod.ts @@ -0,0 +1,25 @@ +/// + +////class Foo { +//// /*a*/bar/*b*/(t: string, s: string): string { +//// return s + t; +//// } +////} +////class Bar extends Foo { } +////var bar = new Bar(); +////bar.bar("a", "b"); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + bar({ t, s }: { t: string; s: string; }): string { + return s + t; + } +} +class Bar extends Foo { } +var bar = new Bar(); +bar.bar({ t: "a", s: "b" });` +}); \ No newline at end of file From 754f4a45b6148ae6757ef971f92b3249d48ecae2 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 19 Feb 2019 14:23:26 -0800 Subject: [PATCH 33/83] refactor expected test output --- ...refactorConvertToNamedParameters_callComments2.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts index e0a78b091fc..37d4e945a2c 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts @@ -21,5 +21,15 @@ edit.applyRefactor({ newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) { return a + b; } -foo({ a: /**a*/ 1, b: /**c*/ 2, rest: [/**e*/ 3, /**g*/ 4] });` +foo( + { + /**a*/ + a: 1, + /**c*/ + b: 2, + rest: [ + /**e*/ + 3, + /**g*/ + 4]});` }); \ No newline at end of file From e40442d46aafbf9ecda1f33b495a2f0457a09a56 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 20 Feb 2019 11:13:04 -0800 Subject: [PATCH 34/83] minor refactors --- .../refactors/convertToNamedParameters.ts | 138 +++++++++--------- 1 file changed, 71 insertions(+), 67 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 9c88e5ad337..8d4f3d6e3c9 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -71,8 +71,8 @@ namespace ts.refactor.convertToNamedParameters { } function getGroupedReferences(functionNames: Node[], program: Program, cancellationToken: CancellationToken): GroupedReferences { - const functionRefs = flatMap(functionNames, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); - const groupedReferences = groupReferences(functionRefs); + const functionReferences = flatMap(functionNames, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); + const groupedReferences = groupReferences(functionReferences); return groupedReferences; function groupReferences(referenceEntries: ReadonlyArray | undefined): GroupedReferences { @@ -97,27 +97,27 @@ namespace ts.refactor.convertToNamedParameters { function entryToFunctionCall(entry: FindAllReferences.Entry): CallExpression | NewExpression | undefined { if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && entry.node.parent) { - const functionRef = entry.node; - const parent = functionRef.parent; + const functionReference = entry.node; + const parent = functionReference.parent; switch (parent.kind) { - // Function call (foo(...)) + // Function call (foo(...) or super(...)) case SyntaxKind.CallExpression: const callExpression = tryCast(parent, isCallExpression); - if (callExpression && callExpression.expression === functionRef) { + if (callExpression && callExpression.expression === functionReference) { return callExpression; } break; // Constructor call (new Foo(...)) case SyntaxKind.NewExpression: const newExpression = tryCast(parent, isNewExpression); - if (newExpression && newExpression.expression === functionRef) { + if (newExpression && newExpression.expression === functionReference) { return newExpression; } break; // Method call (x.foo(...)) case SyntaxKind.PropertyAccessExpression: const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); - if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionRef) { + if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionReference) { const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression); if (callExpression && callExpression.expression === propertyAccessExpression) { return callExpression; @@ -127,7 +127,7 @@ namespace ts.refactor.convertToNamedParameters { // Method call (x['foo'](...)) case SyntaxKind.ElementAccessExpression: const elementAccessExpression = tryCast(parent, isElementAccessExpression); - if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionRef) { + if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionReference) { const callExpression = tryCast(elementAccessExpression.parent, isCallExpression); if (callExpression && callExpression.expression === elementAccessExpression) { return callExpression; @@ -173,30 +173,35 @@ namespace ts.refactor.convertToNamedParameters { } function isValidFunctionDeclaration(functionDeclaration: SignatureDeclaration, checker: TypeChecker): functionDeclaration is ValidFunctionDeclaration { + if (!isValidParameterNodeArray(functionDeclaration.parameters)) return false; switch (functionDeclaration.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: - return !!functionDeclaration.name && isPropertyName(functionDeclaration.name) && isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); + return !!functionDeclaration.name && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); case SyntaxKind.Constructor: if (isClassDeclaration(functionDeclaration.parent)) { - return isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); + return !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); } else { - return isVariableDeclaration(functionDeclaration.parent.parent) && !functionDeclaration.parent.parent.type && isVarConst(functionDeclaration.parent.parent) && isValidParameterNodeArray(functionDeclaration.parameters) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); + return isValidVariableDeclaration(functionDeclaration.parent.parent) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); } case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - return isVariableDeclaration(functionDeclaration.parent) && !functionDeclaration.parent.type && isVarConst(functionDeclaration.parent) && isValidParameterNodeArray(functionDeclaration.parameters); + return isValidVariableDeclaration(functionDeclaration.parent); } return false; function isValidParameterNodeArray(parameters: NodeArray): parameters is ValidParameterNodeArray { - return parameters && getRefactorableParametersLength(parameters) > minimumParameterLength && every(parameters, isValidParameterDeclaration); + return getRefactorableParametersLength(parameters) > minimumParameterLength && every(parameters, isValidParameterDeclaration); } function isValidParameterDeclaration(paramDeclaration: ParameterDeclaration): paramDeclaration is ValidParameterDeclaration { return !paramDeclaration.modifiers && !paramDeclaration.decorators && isIdentifier(paramDeclaration.name); } + + function isValidVariableDeclaration(node: Node): node is ValidVariableDeclaration { + return isVariableDeclaration(node) && isVarConst(node) && !node.type; + } } function hasThisParameter(parameters: NodeArray): boolean { @@ -217,10 +222,10 @@ namespace ts.refactor.convertToNamedParameters { return parameters; } - function createNewArgument(functionDeclaration: ValidFunctionDeclaration, args: NodeArray): ObjectLiteralExpression { + function createNewArgument(functionDeclaration: ValidFunctionDeclaration, functionArguments: NodeArray): ObjectLiteralExpression { const parameters = getRefactorableParameters(functionDeclaration.parameters); const hasRestParameter = isRestParameter(last(parameters)); - const nonRestArguments = hasRestParameter ? args.slice(0, parameters.length - 1) : args; + const nonRestArguments = hasRestParameter ? functionArguments.slice(0, parameters.length - 1) : functionArguments; const properties = map(nonRestArguments, (arg, i) => { const property = createPropertyAssignment(getParameterName(parameters[i]), arg); suppressLeadingAndTrailingTrivia(property.initializer); @@ -228,8 +233,8 @@ namespace ts.refactor.convertToNamedParameters { return property; }); - if (hasRestParameter && args.length >= parameters.length) { - const restArguments = args.slice(parameters.length - 1); + if (hasRestParameter && functionArguments.length >= parameters.length) { + const restArguments = functionArguments.slice(parameters.length - 1); const restProperty = createPropertyAssignment(getParameterName(last(parameters)), createArrayLiteral(restArguments)); properties.push(restProperty); } @@ -240,82 +245,83 @@ namespace ts.refactor.convertToNamedParameters { function createNewParameters(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray { const refactorableParameters = getRefactorableParameters(functionDeclaration.parameters); - const bindingElements = map( - refactorableParameters, - paramDecl => { - const element = createBindingElement( - /*dotDotDotToken*/ undefined, - /*propertyName*/ undefined, - getParameterName(paramDecl), - isRestParameter(paramDecl) ? createArrayLiteral() : paramDecl.initializer); - - suppressLeadingAndTrailingTrivia(element); - if (paramDecl.initializer && element.initializer) { - copyComments(paramDecl.initializer, element.initializer); - } - - return element; }); - const paramName = createObjectBindingPattern(bindingElements); - const paramType = createParamTypeNode(refactorableParameters); + const bindingElements = map(refactorableParameters, createBindingElementFromParameterDeclaration); + const objectParameterName = createObjectBindingPattern(bindingElements); + const objectParameterType = createParameterTypeNode(refactorableParameters); let objectInitializer: Expression | undefined; + // If every parameter in the original function was optional, add an empty object initializer to the new object parameter if (every(refactorableParameters, param => !!param.initializer || !!param.questionToken)) { objectInitializer = createObjectLiteral(); } - const newParameter = createParameter( + const objectParameter = createParameter( /*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, - paramName, + objectParameterName, /*questionToken*/ undefined, - paramType, + objectParameterType, objectInitializer); if (hasThisParameter(functionDeclaration.parameters)) { - const thisParam = functionDeclaration.parameters[0]; - const newThis = createParameter( + const thisParameter = functionDeclaration.parameters[0]; + const newThisParameter = createParameter( /*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, - thisParam.name, + thisParameter.name, /*questionToken*/ undefined, - thisParam.type); + thisParameter.type); - suppressLeadingAndTrailingTrivia(newThis.name); - copyComments(thisParam.name, newThis.name); - if (thisParam.type && newThis.type) { - suppressLeadingAndTrailingTrivia(newThis.type); - copyComments(thisParam.type, newThis.type); + suppressLeadingAndTrailingTrivia(newThisParameter.name); + copyComments(thisParameter.name, newThisParameter.name); + if (thisParameter.type && newThisParameter.type) { + suppressLeadingAndTrailingTrivia(newThisParameter.type); + copyComments(thisParameter.type, newThisParameter.type); } - return createNodeArray([newThis, newParameter]); + return createNodeArray([newThisParameter, objectParameter]); } - return createNodeArray([newParameter]); + return createNodeArray([objectParameter]); - function createParamTypeNode(parameters: NodeArray): TypeLiteralNode { + function createBindingElementFromParameterDeclaration(parameterDeclaration: ValidParameterDeclaration): BindingElement { + const element = createBindingElement( + /*dotDotDotToken*/ undefined, + /*propertyName*/ undefined, + getParameterName(parameterDeclaration), + isRestParameter(parameterDeclaration) ? createArrayLiteral() : parameterDeclaration.initializer); + + suppressLeadingAndTrailingTrivia(element); + if (parameterDeclaration.initializer && element.initializer) { + copyComments(parameterDeclaration.initializer, element.initializer); + } + return element; + } + + function createParameterTypeNode(parameters: NodeArray): TypeLiteralNode { const members = map(parameters, createPropertySignatureFromParameterDeclaration); - const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); // TODO: add single line option to createTypeLiteralNode + const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); return typeNode; } - function createPropertySignatureFromParameterDeclaration(paramDeclaration: ValidParameterDeclaration): PropertySignature { - let paramType = paramDeclaration.type; - if (!paramType && (paramDeclaration.initializer || isRestParameter(paramDeclaration))) { - paramType = getTypeNode(paramDeclaration); + function createPropertySignatureFromParameterDeclaration(parameterDeclaration: ValidParameterDeclaration): PropertySignature { + let parameterType = parameterDeclaration.type; + if (!parameterType && (parameterDeclaration.initializer || isRestParameter(parameterDeclaration))) { + parameterType = getTypeNode(parameterDeclaration); } const propertySignature = createPropertySignature( /*modifiers*/ undefined, - getParameterName(paramDeclaration), - paramDeclaration.initializer || isRestParameter(paramDeclaration) ? createToken(SyntaxKind.QuestionToken) : paramDeclaration.questionToken, - paramType, + getParameterName(parameterDeclaration), + parameterDeclaration.initializer || isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : parameterDeclaration.questionToken, + parameterType, /*initializer*/ undefined); suppressLeadingAndTrailingTrivia(propertySignature); - copyComments(paramDeclaration.name, propertySignature.name); - if (paramDeclaration.type && propertySignature.type) { - copyComments(paramDeclaration.type, propertySignature.type); + copyComments(parameterDeclaration.name, propertySignature.name); + if (parameterDeclaration.type && propertySignature.type) { + copyComments(parameterDeclaration.type, propertySignature.type); } return propertySignature; @@ -359,15 +365,13 @@ namespace ts.refactor.convertToNamedParameters { case SyntaxKind.MethodDeclaration: return [functionDeclaration.name]; case SyntaxKind.Constructor: - const ctrKeyword = findChildOfKind(functionDeclaration, SyntaxKind.ConstructorKeyword, functionDeclaration.getSourceFile()); - let name: Node; + const ctrKeyword = findChildOfKind(functionDeclaration, SyntaxKind.ConstructorKeyword, functionDeclaration.getSourceFile())!; switch (functionDeclaration.parent.kind) { case SyntaxKind.ClassDeclaration: - return [ctrKeyword!]; + return [ctrKeyword]; case SyntaxKind.ClassExpression: - name = functionDeclaration.parent.parent.name; - if (ctrKeyword) return [ctrKeyword, name]; - return [name]; + const name = functionDeclaration.parent.parent.name; + return [ctrKeyword, name]; default: return Debug.assertNever(functionDeclaration.parent); } case SyntaxKind.ArrowFunction: From ce42aa43a8b11a1fdae4313225d9e6012b3d70e7 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 22 Feb 2019 16:31:40 -0800 Subject: [PATCH 35/83] check usages of class if refactoring a constructor --- .../refactors/convertToNamedParameters.ts | 251 ++++++++++++------ 1 file changed, 166 insertions(+), 85 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 8d4f3d6e3c9..9bcea0e5983 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -31,9 +31,8 @@ namespace ts.refactor.convertToNamedParameters { const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker()); if (!functionDeclaration || !cancellationToken) return undefined; - const functionNames = getFunctionDeclarationNames(functionDeclaration); - const groupedReferences = getGroupedReferences(functionNames, program, cancellationToken); - if (checkReferences(functionNames, groupedReferences)) { + const groupedReferences = getGroupedReferences(functionDeclaration, program, cancellationToken); + if (groupedReferences.valid) { const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, host, t, functionDeclaration, groupedReferences)); return { renameFilename: undefined, renameLocation: undefined, edits }; } @@ -41,7 +40,6 @@ namespace ts.refactor.convertToNamedParameters { return { edits: [] }; } - function doChange(sourceFile: SourceFile, program: Program, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration, groupedReferences: GroupedReferences): void { const newParamDeclaration = map(createNewParameters(functionDeclaration, program, host), param => getSynthesizedDeepClone(param)); changes.replaceNodeRangeWithNodes( @@ -57,7 +55,7 @@ namespace ts.refactor.convertToNamedParameters { }); - const functionCalls = groupedReferences.calls; + const functionCalls = deduplicate(groupedReferences.functionCalls, (a, b) => a === b); forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { const newArgument = getSynthesizedDeepClone(createNewArgument(functionDeclaration, call.arguments), /*includeTrivia*/ true); @@ -70,99 +68,172 @@ namespace ts.refactor.convertToNamedParameters { }}); } - function getGroupedReferences(functionNames: Node[], program: Program, cancellationToken: CancellationToken): GroupedReferences { - const functionReferences = flatMap(functionNames, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); - const groupedReferences = groupReferences(functionReferences); + function getGroupedReferences(functionDeclaration: ValidFunctionDeclaration, program: Program, cancellationToken: CancellationToken): GroupedReferences { + const names = getDeclarationNames(functionDeclaration); + const references = flatMap(names, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); + let groupedReferences = groupReferences(references); + + // if the refactored function is a constructor, we must also go through the references to its class + if (isConstructorDeclaration(functionDeclaration)) { + const className = getClassName(functionDeclaration); + groupedReferences = groupClassReferences(groupedReferences, className); + } + + validateReferences(groupedReferences); return groupedReferences; + function getClassName(constructorDeclaration: ValidConstructor): Identifier { + switch (constructorDeclaration.parent.kind) { + case SyntaxKind.ClassDeclaration: + return constructorDeclaration.parent.name; + case SyntaxKind.ClassExpression: + return constructorDeclaration.parent.parent.name; + } + } + function groupReferences(referenceEntries: ReadonlyArray | undefined): GroupedReferences { - const references: GroupedReferences = { calls: [], declarations: [], unhandled: [] }; + const groupedReferences: GroupedReferences = { functionCalls: [], declarations: [], unhandled: [], valid: true }; + forEach(referenceEntries, (entry) => { - const decl = entryToDeclarationName(entry); + const decl = entryToDeclaration(entry); if (decl) { - references.declarations.push(decl); + groupedReferences.declarations.push(decl); return; } + const call = entryToFunctionCall(entry); if (call) { - references.calls.push(call); + groupedReferences.functionCalls.push(call); return; } - const node = entryToNode(entry); - if (node) { - references.unhandled.push(node); - } - }); - return references; - function entryToFunctionCall(entry: FindAllReferences.Entry): CallExpression | NewExpression | undefined { - if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && entry.node.parent) { - const functionReference = entry.node; - const parent = functionReference.parent; - switch (parent.kind) { - // Function call (foo(...) or super(...)) - case SyntaxKind.CallExpression: - const callExpression = tryCast(parent, isCallExpression); - if (callExpression && callExpression.expression === functionReference) { - return callExpression; - } - break; - // Constructor call (new Foo(...)) - case SyntaxKind.NewExpression: - const newExpression = tryCast(parent, isNewExpression); - if (newExpression && newExpression.expression === functionReference) { - return newExpression; - } - break; - // Method call (x.foo(...)) - case SyntaxKind.PropertyAccessExpression: - const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); - if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionReference) { - const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression); - if (callExpression && callExpression.expression === propertyAccessExpression) { - return callExpression; - } - } - break; - // Method call (x['foo'](...)) - case SyntaxKind.ElementAccessExpression: - const elementAccessExpression = tryCast(parent, isElementAccessExpression); - if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionReference) { - const callExpression = tryCast(elementAccessExpression.parent, isCallExpression); - if (callExpression && callExpression.expression === elementAccessExpression) { - return callExpression; - } - } - break; + groupedReferences.unhandled.push(entry); + }); + return groupedReferences; + } + + function groupClassReferences(groupedReferences: GroupedReferences, className: Identifier): GroupedReferences { + const classReferences: ClassReferences = { accessExpressions: [], typeUsages: [] }; + const unhandledEntries = groupedReferences.unhandled; + const newUnhandledEntries: FindAllReferences.Entry[] = []; + + forEach(unhandledEntries, (entry) => { + if (entry.kind === FindAllReferences.EntryKind.Node && entry.node.symbol === className.symbol) { + const accessExpression = entryToAccessExpression(entry); + if (accessExpression) { + classReferences.accessExpressions.push(accessExpression); + return; + } + + // Only class declarations are allowed to be used as a type (in a heritage clause), + // otherwise `findAllReferences` might not be able to track constructor calls. + if (isClassDeclaration(functionDeclaration.parent)) { + const type = entryToType(entry); + if (type) { + classReferences.typeUsages.push(type); + return; + } } } - return undefined; - } + newUnhandledEntries.push(entry); + }); - function entryToDeclarationName(entry: FindAllReferences.Entry): Node | undefined { - if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node && contains(functionNames, entry.node)) { - return entry.node; + return { ...groupedReferences, classReferences, unhandled: newUnhandledEntries }; + } + + function validateReferences(groupedReferences: GroupedReferences): void { + if (groupedReferences.unhandled.length > 0) { + groupedReferences.valid = false; + } + if (!every(groupedReferences.declarations, decl => contains(names, decl))) { + groupedReferences.valid = false; + } + } + + function entryToFunctionCall(entry: FindAllReferences.Entry): CallExpression | NewExpression | undefined { + if (entry.kind === FindAllReferences.EntryKind.Node && entry.node.parent) { + const functionReference = entry.node; + const parent = functionReference.parent; + switch (parent.kind) { + // Function call (foo(...) or super(...)) + case SyntaxKind.CallExpression: + const callExpression = tryCast(parent, isCallExpression); + if (callExpression && callExpression.expression === functionReference) { + return callExpression; + } + break; + // Constructor call (new Foo(...)) + case SyntaxKind.NewExpression: + const newExpression = tryCast(parent, isNewExpression); + if (newExpression && newExpression.expression === functionReference) { + return newExpression; + } + break; + // Method call (x.foo(...)) + case SyntaxKind.PropertyAccessExpression: + const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); + if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionReference) { + const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression); + if (callExpression && callExpression.expression === propertyAccessExpression) { + return callExpression; + } + } + break; + // Method call (x["foo"](...)) + case SyntaxKind.ElementAccessExpression: + const elementAccessExpression = tryCast(parent, isElementAccessExpression); + if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionReference) { + const callExpression = tryCast(elementAccessExpression.parent, isCallExpression); + if (callExpression && callExpression.expression === elementAccessExpression) { + return callExpression; + } + } + break; } - return undefined; } + return undefined; + } - function entryToNode(entry: FindAllReferences.Entry): Node | undefined { - if (entry.kind !== FindAllReferences.EntryKind.Span && entry.node) { - return entry.node; + function entryToDeclaration(entry: FindAllReferences.Entry): Node | undefined { + if (entry.kind === FindAllReferences.EntryKind.Node && contains(names, entry.node)) { + return entry.node; + } + return undefined; + } + + function entryToAccessExpression(entry: FindAllReferences.Entry): ElementAccessExpression | PropertyAccessExpression | undefined { + if (entry.kind === FindAllReferences.EntryKind.Node && entry.node.parent) { + const reference = entry.node; + const parent = reference.parent; + switch (parent.kind) { + // `C.foo` + case SyntaxKind.PropertyAccessExpression: + const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); + if (propertyAccessExpression && propertyAccessExpression.expression === reference) { + return propertyAccessExpression; + } + break; + // `C["foo"]` + case SyntaxKind.ElementAccessExpression: + const elementAccessExpression = tryCast(parent, isElementAccessExpression); + if (elementAccessExpression && elementAccessExpression.expression === reference) { + return elementAccessExpression; + } + break; } - return undefined; } + return undefined; } - } - function checkReferences(functionNames: Node[], groupedReferences: GroupedReferences): boolean { - if (groupedReferences.unhandled.length > 0) { - return false; + function entryToType(entry: FindAllReferences.Entry): Node | undefined { + if (entry.kind === FindAllReferences.EntryKind.Node) { + const reference = entry.node; + if (getMeaningFromLocation(reference) === SemanticMeaning.Type || isExpressionWithTypeArgumentsInClassExtendsClause(reference.parent)) { + return reference; + } + } + return undefined; } - if (groupedReferences.declarations.length > functionNames.length) { - return false; - } - return true; } function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined { @@ -180,7 +251,7 @@ namespace ts.refactor.convertToNamedParameters { return !!functionDeclaration.name && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); case SyntaxKind.Constructor: if (isClassDeclaration(functionDeclaration.parent)) { - return !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); + return !!functionDeclaration.body && !!functionDeclaration.parent.name && !checker.isImplementationOfOverload(functionDeclaration); } else { return isValidVariableDeclaration(functionDeclaration.parent.parent) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration); @@ -200,7 +271,7 @@ namespace ts.refactor.convertToNamedParameters { } function isValidVariableDeclaration(node: Node): node is ValidVariableDeclaration { - return isVariableDeclaration(node) && isVarConst(node) && !node.type; + return isVariableDeclaration(node) && isVarConst(node) && isIdentifier(node.name) && !node.type; } } @@ -359,7 +430,7 @@ namespace ts.refactor.convertToNamedParameters { return getTextOfIdentifierOrLiteral(paramDeclaration.name); } - function getFunctionDeclarationNames(functionDeclaration: ValidFunctionDeclaration): Node[] { + function getDeclarationNames(functionDeclaration: ValidFunctionDeclaration): Node[] { switch (functionDeclaration.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: @@ -368,10 +439,14 @@ namespace ts.refactor.convertToNamedParameters { const ctrKeyword = findChildOfKind(functionDeclaration, SyntaxKind.ConstructorKeyword, functionDeclaration.getSourceFile())!; switch (functionDeclaration.parent.kind) { case SyntaxKind.ClassDeclaration: - return [ctrKeyword]; + const classDeclaration = functionDeclaration.parent; + return [classDeclaration.name, ctrKeyword]; case SyntaxKind.ClassExpression: - const name = functionDeclaration.parent.parent.name; - return [ctrKeyword, name]; + const classExpression = functionDeclaration.parent; + const variableDeclaration = functionDeclaration.parent.parent; + const className = classExpression.name; + if (className) return [className, ctrKeyword, variableDeclaration.name]; + return [ctrKeyword, variableDeclaration.name]; default: return Debug.assertNever(functionDeclaration.parent); } case SyntaxKind.ArrowFunction: @@ -384,10 +459,10 @@ namespace ts.refactor.convertToNamedParameters { type ValidParameterNodeArray = NodeArray; - type ValidVariableDeclaration = VariableDeclaration & { type: undefined }; + type ValidVariableDeclaration = VariableDeclaration & { name: Identifier, type: undefined }; interface ValidConstructor extends ConstructorDeclaration { - parent: ClassDeclaration | (ClassExpression & { parent: ValidVariableDeclaration }); + parent: (ClassDeclaration & { name: Identifier }) | (ClassExpression & { parent: ValidVariableDeclaration }); parameters: NodeArray; body: FunctionBody; } @@ -422,8 +497,14 @@ namespace ts.refactor.convertToNamedParameters { } interface GroupedReferences { - calls: (CallExpression | NewExpression)[]; + functionCalls: (CallExpression | NewExpression)[]; declarations: Node[]; - unhandled: Node[]; + classReferences?: ClassReferences; + unhandled: FindAllReferences.Entry[]; + valid: boolean; + } + interface ClassReferences { + accessExpressions: Node[]; + typeUsages: Node[]; } } \ No newline at end of file From 640424e42cb73ebaa63f10453afab7141dfa2bc2 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 22 Feb 2019 16:32:12 -0800 Subject: [PATCH 36/83] fix comment --- src/compiler/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 4bd4801ce8f..a47387a932e 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -917,7 +917,7 @@ namespace ts { /** * Deduplicates an unsorted array. - * @param equalityComparer An optional `EqualityComparer` used to determine if two values are duplicates. + * @param equalityComparer An `EqualityComparer` used to determine if two values are duplicates. * @param comparer An optional `Comparer` used to sort entries before comparison, though the * result will remain in the original order in `array`. */ From bc3611d1dd0ba1f899ab824ffe60b64a8b694fa5 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 25 Feb 2019 10:32:28 -0800 Subject: [PATCH 37/83] update failing tests --- ...efactorConvertToNamedParameters_callComments2.ts | 13 ++++++------- ...ConvertToNamedParameters_inheritedConstructor.ts | 7 +++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts index 37d4e945a2c..96a56d7024d 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_callComments2.ts @@ -14,6 +14,7 @@ //// 4); goTo.select("a", "b"); +/* The expected content is currently wrong. The new argument object has the wrong formatting. */ edit.applyRefactor({ refactorName: "Convert to named parameters", actionName: "Convert to named parameters", @@ -22,14 +23,12 @@ edit.applyRefactor({ return a + b; } foo( - { - /**a*/ - a: 1, - /**c*/ - b: 2, - rest: [ + { /**a*/ + a: 1, /**c*/ + b: 2, rest: [ /**e*/ 3, /**g*/ - 4]});` + 4] + });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedConstructor.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedConstructor.ts index 743fc7fadb4..bde7839a60e 100644 --- a/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedConstructor.ts +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_inheritedConstructor.ts @@ -8,6 +8,9 @@ ////var foo = new Foo("c", "d"); goTo.select("a", "b"); +/* The expected new content is currently wrong. + `new Bar("a", "b")` should be modified by the refactor to be `new Bar({ t: "a", s: "b" })` +*/ edit.applyRefactor({ refactorName: "Convert to named parameters", actionName: "Convert to named parameters", @@ -16,6 +19,6 @@ edit.applyRefactor({ constructor({ t, s }: { t: string; s: string; }) { } } class Bar extends Foo { } -var bar = new Bar({ t: "a", s: "b" }); -var foo = new Foo({ t: "c", s: "d" })` +var bar = new Bar("a", "b"); +var foo = new Foo({ t: "c", s: "d" });` }); \ No newline at end of file From f571866f47cae625918574873f65979e2d4f3850 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 25 Feb 2019 10:32:56 -0800 Subject: [PATCH 38/83] add new tests for bad class references --- ...amedParameters_classDeclarationAliasing.ts | 20 ++++++++++++++ ...NamedParameters_classExpressionHeritage.ts | 26 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_classDeclarationAliasing.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_classExpressionHeritage.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_classDeclarationAliasing.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_classDeclarationAliasing.ts new file mode 100644 index 00000000000..0914dedd57d --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_classDeclarationAliasing.ts @@ -0,0 +1,20 @@ +/// + +////class Foo { +//// /*a*/constructor/*b*/(a: number, b: number) { } +////} +////const fooAlias = Foo; +////const newFoo = new fooAlias(1, 2); + +goTo.select("a", "b"); +// Refactor should not make changes +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class Foo { + constructor(a: number, b: number) { } +} +const fooAlias = Foo; +const newFoo = new fooAlias(1, 2);` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_classExpressionHeritage.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_classExpressionHeritage.ts new file mode 100644 index 00000000000..14b9639c2ad --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_classExpressionHeritage.ts @@ -0,0 +1,26 @@ +/// + +////const foo = class Foo { +//// /*a*/constructor/*b*/(a: number, b: number) { } +////} +////class Bar extends foo { +//// constructor() { +//// super(1, 2); +//// } +////} + +goTo.select("a", "b"); +// Refactor should not make changes +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `const foo = class Foo { + constructor(a: number, b: number) { } +} +class Bar extends foo { + constructor() { + super(1, 2); + } +}` +}); \ No newline at end of file From a5153a94ab505747c96a4bd0a4704a93796c81a5 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 25 Feb 2019 14:14:28 -0800 Subject: [PATCH 39/83] add tests --- ...edParameters_classDeclarationGoodUsages.ts | 31 +++++++++++++++++++ ...medParameters_classExpressionGoodUsages.ts | 23 ++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_classDeclarationGoodUsages.ts create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_classExpressionGoodUsages.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_classDeclarationGoodUsages.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_classDeclarationGoodUsages.ts new file mode 100644 index 00000000000..858bccd60c1 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_classDeclarationGoodUsages.ts @@ -0,0 +1,31 @@ +/// + +////class C { +//// static a: number = 2; +//// /*a*/constructor/*b*/(a: number, b: number) { } +////} +////const newC = new C(1, 2); +////const b = C.a; +////C["a"] = 3; +////let c: C; +////function f(c: C) { } +////class B extends C { } +////class A implements C { } + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class C { + static a: number = 2; + constructor({ a, b }: { a: number; b: number; }) { } +} +const newC = new C({ a: 1, b: 2 }); +const b = C.a; +C["a"] = 3; +let c: C; +function f(c: C) { } +class B extends C { } +class A implements C { }` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_classExpressionGoodUsages.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_classExpressionGoodUsages.ts new file mode 100644 index 00000000000..f8023b9898c --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_classExpressionGoodUsages.ts @@ -0,0 +1,23 @@ +/// + +////const c = class C { +//// static a: number = 2; +//// /*a*/constructor/*b*/(a: number, b: number) { } +////} +////const a = new c(0, 1); +////const b = c.a; +////c["a"] = 3; + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `const c = class C { + static a: number = 2; + constructor({ a, b }: { a: number; b: number; }) { } +} +const a = new c({ a: 0, b: 1 }); +const b = c.a; +c["a"] = 3;` +}); \ No newline at end of file From e3a465ffa3136e2676cf2af1ac978c8e814b7b74 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 26 Feb 2019 09:43:35 -0800 Subject: [PATCH 40/83] change startPosition and endPosition to leadingTriviaOption and trailingTriviaOption --- src/services/organizeImports.ts | 4 +-- .../refactors/convertToNamedParameters.ts | 6 ++-- src/services/textChanges.ts | 35 +++++++++---------- .../unittests/services/textChanges.ts | 26 +++++++------- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index 8096ffa4ad2..5bfa0c74ab1 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -68,8 +68,8 @@ namespace ts.OrganizeImports { else { // Note: Delete the surrounding trivia because it will have been retained in newImportDecls. changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, { - startPosition: textChanges.LeadingTriviaOption.Exclude, // Leave header comment in place - endPosition: textChanges.TrailingTriviaOption.Include, + leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, // Leave header comment in place + trailingTriviaOption: textChanges.TrailingTriviaOption.Include, suffix: getNewLineOrDefaultFromHost(host, formatContext.options), }); } diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 9bcea0e5983..fa285619dab 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -50,8 +50,8 @@ namespace ts.refactor.convertToNamedParameters { { joiner: ", ", // indentation is set to 0 because otherwise the object parameter will be indented if there is a `this` parameter indentation: 0, - startPosition: textChanges.LeadingTriviaOption.IncludeAll, - endPosition: textChanges.TrailingTriviaOption.Include + leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, + trailingTriviaOption: textChanges.TrailingTriviaOption.Include }); @@ -64,7 +64,7 @@ namespace ts.refactor.convertToNamedParameters { first(call.arguments), last(call.arguments), newArgument, - { startPosition: textChanges.LeadingTriviaOption.IncludeAll, endPosition: textChanges.TrailingTriviaOption.Include }); + { leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, trailingTriviaOption: textChanges.TrailingTriviaOption.Include }); }}); } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index b8e8ee90ee9..529fcef51de 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -28,11 +28,10 @@ namespace ts.textChanges { } export interface ConfigurableStart { - startPosition?: LeadingTriviaOption; + leadingTriviaOption?: LeadingTriviaOption; } export interface ConfigurableEnd { - /** True to use getEnd() without adjustment. */ - endPosition?: TrailingTriviaOption; + trailingTriviaOption?: TrailingTriviaOption; } export enum LeadingTriviaOption { @@ -48,7 +47,7 @@ namespace ts.textChanges { } export enum TrailingTriviaOption { - /** Exclude all leading trivia (use getEnd()) */ + /** Exclude all trailing trivia (use getEnd()) */ Exclude, /** TODO (default behavior) */ IncludeIfLineBreak, @@ -88,8 +87,8 @@ namespace ts.textChanges { export interface ConfigurableStartEnd extends ConfigurableStart, ConfigurableEnd {} export const useNonAdjustedPositions: ConfigurableStartEnd = { - startPosition: LeadingTriviaOption.Exclude, - endPosition: TrailingTriviaOption.Exclude, + leadingTriviaOption: LeadingTriviaOption.Exclude, + trailingTriviaOption: TrailingTriviaOption.Exclude, }; export interface InsertNodeOptions { @@ -162,8 +161,8 @@ namespace ts.textChanges { } function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStart) { - const { startPosition } = options; - if (startPosition === LeadingTriviaOption.Exclude) { + const { leadingTriviaOption } = options; + if (leadingTriviaOption === LeadingTriviaOption.Exclude) { return node.getStart(sourceFile); } const fullStart = node.getFullStart(); @@ -181,7 +180,7 @@ namespace ts.textChanges { // fullstart // when b is replaced - we usually want to keep the leading trvia // when b is deleted - we delete it - return startPosition === LeadingTriviaOption.IncludeAll ? fullStart : start; + return leadingTriviaOption === LeadingTriviaOption.IncludeAll ? fullStart : start; } // get start position of the line following the line that contains fullstart position // (but only if the fullstart isn't the very beginning of the file) @@ -194,12 +193,12 @@ namespace ts.textChanges { function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) { const { end } = node; - const { endPosition } = options; - if (endPosition === TrailingTriviaOption.Exclude || (isExpression(node) && endPosition !== TrailingTriviaOption.Include)) { + const { trailingTriviaOption } = options; + if (trailingTriviaOption === TrailingTriviaOption.Exclude || (isExpression(node) && trailingTriviaOption !== TrailingTriviaOption.Include)) { return end; } const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true); - return newEnd !== end && (endPosition === TrailingTriviaOption.Include || isLineBreak(sourceFile.text.charCodeAt(newEnd - 1))) + return newEnd !== end && (trailingTriviaOption === TrailingTriviaOption.Include || isLineBreak(sourceFile.text.charCodeAt(newEnd - 1))) ? newEnd : end; } @@ -257,13 +256,13 @@ namespace ts.textChanges { this.deleteRange(sourceFile, { pos: modifier.getStart(sourceFile), end: skipTrivia(sourceFile.text, modifier.end, /*stopAfterLineBreak*/ true) }); } - public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = { startPosition: LeadingTriviaOption.IncludeAll }): void { + public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }): void { const startPosition = getAdjustedStartPosition(sourceFile, startNode, options); const endPosition = getAdjustedEndPosition(sourceFile, endNode, options); this.deleteRange(sourceFile, { pos: startPosition, end: endPosition }); } - public deleteNodeRangeExcludingEnd(sourceFile: SourceFile, startNode: Node, afterEndNode: Node | undefined, options: ConfigurableStartEnd = { startPosition: LeadingTriviaOption.IncludeAll }): void { + public deleteNodeRangeExcludingEnd(sourceFile: SourceFile, startNode: Node, afterEndNode: Node | undefined, options: ConfigurableStartEnd = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }): void { const startPosition = getAdjustedStartPosition(sourceFile, startNode, options); const endPosition = afterEndNode === undefined ? sourceFile.text.length : getAdjustedStartPosition(sourceFile, afterEndNode, options); this.deleteRange(sourceFile, { pos: startPosition, end: endPosition }); @@ -753,7 +752,7 @@ namespace ts.textChanges { // find first non-whitespace position in the leading trivia of the node function startPositionToDeleteNodeInList(sourceFile: SourceFile, node: Node): number { - return skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, { startPosition: LeadingTriviaOption.IncludeAll }), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); + return skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, { leadingTriviaOption: LeadingTriviaOption.IncludeAll }), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); } function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, sourceFile: SourceFile): [number, number] { @@ -1107,7 +1106,7 @@ namespace ts.textChanges { case SyntaxKind.ImportDeclaration: deleteNode(changes, sourceFile, node, // For first import, leave header comment in place - node === sourceFile.imports[0].parent ? { startPosition: LeadingTriviaOption.Exclude, endPosition: TrailingTriviaOption.IncludeIfLineBreak } : undefined); + node === sourceFile.imports[0].parent ? { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.IncludeIfLineBreak } : undefined); break; case SyntaxKind.BindingElement: @@ -1151,7 +1150,7 @@ namespace ts.textChanges { deleteNodeInList(changes, deletedNodesInLists, sourceFile, node); } else { - deleteNode(changes, sourceFile, node, node.kind === SyntaxKind.SemicolonToken ? { endPosition: TrailingTriviaOption.Exclude } : undefined); + deleteNode(changes, sourceFile, node, node.kind === SyntaxKind.SemicolonToken ? { trailingTriviaOption: TrailingTriviaOption.Exclude } : undefined); } } } @@ -1230,7 +1229,7 @@ namespace ts.textChanges { /** Warning: This deletes comments too. See `copyComments` in `convertFunctionToEs6Class`. */ // Exported for tests only! (TODO: improve tests to not need this) - export function deleteNode(changes: ChangeTracker, sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = { startPosition: LeadingTriviaOption.IncludeAll }): void { + export function deleteNode(changes: ChangeTracker, sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }): void { const startPosition = getAdjustedStartPosition(sourceFile, node, options); const endPosition = getAdjustedEndPosition(sourceFile, node, options); changes.deleteRange(sourceFile, { pos: startPosition, end: endPosition }); diff --git a/src/testRunner/unittests/services/textChanges.ts b/src/testRunner/unittests/services/textChanges.ts index f9e03a578de..27195c30034 100644 --- a/src/testRunner/unittests/services/textChanges.ts +++ b/src/testRunner/unittests/services/textChanges.ts @@ -140,13 +140,13 @@ var z = 3; // comment 4 deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile)); }); runSingleFileTest("deleteNode2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { startPosition: textChanges.LeadingTriviaOption.Exclude }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { endPosition: textChanges.TrailingTriviaOption.Exclude }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode5", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { deleteNode(changeTracker, sourceFile, findVariableStatementContaining("x", sourceFile)); @@ -167,15 +167,15 @@ var a = 4; // comment 7 }); runSingleFileTest("deleteNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { startPosition: textChanges.LeadingTriviaOption.Exclude }); + { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude }); }); runSingleFileTest("deleteNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { endPosition: textChanges.TrailingTriviaOption.Exclude }); + { trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); + { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude }); }); } function createTestVariableDeclaration(name: string) { @@ -254,16 +254,16 @@ var a = 4; // comment 7`; changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter }); }); runSingleFileTest("replaceNode2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); }); runSingleFileTest("replaceNode3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { endPosition: textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter }); }); runSingleFileTest("replaceNode4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude }); }); runSingleFileTest("replaceNode5", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude }); }); } { @@ -279,13 +279,13 @@ var a = 4; // comment 7`; changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { suffix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { endPosition: textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { startPosition: textChanges.LeadingTriviaOption.Exclude, endPosition: textChanges.TrailingTriviaOption.Exclude }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude }); }); } { From bf5123de6acd8159a584ec2103792f988731f8ae Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 26 Feb 2019 09:49:10 -0800 Subject: [PATCH 41/83] don't export useNonAjustedPositions --- src/services/textChanges.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 529fcef51de..3b08edd6733 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -82,11 +82,12 @@ namespace ts.textChanges { * Usually leading trivia of the variable declaration 'y' should not include trailing trivia (whitespace, comment 'this is x' and newline) from the preceding * variable declaration and trailing trivia for 'y' should include (whitespace, comment 'this is y', newline). * By default when removing nodes we adjust start and end positions to respect specification of the trivia above. - * If pos\end should be interpreted literally 'useNonAdjustedStartPosition' or 'useNonAdjustedEndPosition' should be set to true + * If pos\end should be interpreted literally (that is, withouth including leading and trailing trivia), `leadingTriviaOption` should be set to `LeadingTriviaOption.Exclude` + * and `trailingTriviaOption` to `TrailingTriviaOption.Exclude`. */ export interface ConfigurableStartEnd extends ConfigurableStart, ConfigurableEnd {} - export const useNonAdjustedPositions: ConfigurableStartEnd = { + const useNonAdjustedPositions: ConfigurableStartEnd = { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.Exclude, }; From 970ec62364c532e606f9b6e9d824a6d7e00edb93 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 26 Feb 2019 10:02:57 -0800 Subject: [PATCH 42/83] remove unused LeadingTriviaOption and TrailingTriviaOption options --- src/services/textChanges.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 3b08edd6733..81441627635 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -37,8 +37,6 @@ namespace ts.textChanges { export enum LeadingTriviaOption { /** Exclude all leading trivia (use getStart()) */ Exclude, - /** Include leading trivia (default behavior) */ - Include, /** Include leading trivia and, * if there are no line breaks between the node and the previous token, * include all trivia between the node and the previous token @@ -49,8 +47,6 @@ namespace ts.textChanges { export enum TrailingTriviaOption { /** Exclude all trailing trivia (use getEnd()) */ Exclude, - /** TODO (default behavior) */ - IncludeIfLineBreak, /** Include trailing trivia */ Include, } @@ -83,7 +79,7 @@ namespace ts.textChanges { * variable declaration and trailing trivia for 'y' should include (whitespace, comment 'this is y', newline). * By default when removing nodes we adjust start and end positions to respect specification of the trivia above. * If pos\end should be interpreted literally (that is, withouth including leading and trailing trivia), `leadingTriviaOption` should be set to `LeadingTriviaOption.Exclude` - * and `trailingTriviaOption` to `TrailingTriviaOption.Exclude`. + * and `trailingTriviaOption` to `TrailingTriviaOption.Exclude`. */ export interface ConfigurableStartEnd extends ConfigurableStart, ConfigurableEnd {} @@ -1107,7 +1103,7 @@ namespace ts.textChanges { case SyntaxKind.ImportDeclaration: deleteNode(changes, sourceFile, node, // For first import, leave header comment in place - node === sourceFile.imports[0].parent ? { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.IncludeIfLineBreak } : undefined); + node === sourceFile.imports[0].parent ? { leadingTriviaOption: LeadingTriviaOption.Exclude } : undefined); break; case SyntaxKind.BindingElement: From 6fd7011870083105c05aa91591cac88d6766427b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 25 Feb 2019 13:07:15 -0800 Subject: [PATCH 43/83] Add test that batches all the open files to update program only once --- src/testRunner/tsconfig.json | 1 + .../tsserver/applyChangesToOpenFiles.ts | 108 ++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 137e4d8af58..4e5c088025c 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -96,6 +96,7 @@ "unittests/tscWatch/resolutionCache.ts", "unittests/tscWatch/watchEnvironment.ts", "unittests/tscWatch/watchApi.ts", + "unittests/tsserver/applyChangesToOpenFiles.ts", "unittests/tsserver/cachingFileSystemInformation.ts", "unittests/tsserver/cancellationToken.ts", "unittests/tsserver/compileOnSave.ts", diff --git a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts new file mode 100644 index 00000000000..f964e77e0eb --- /dev/null +++ b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts @@ -0,0 +1,108 @@ +namespace ts.projectSystem { + describe("unittests:: tsserver:: applyChangesToOpenFiles", () => { + const configFile: File = { + path: "/a/b/tsconfig.json", + content: "{}" + }; + const file3: File = { + path: "/a/b/file3.ts", + content: "let xyz = 1;" + }; + const app: File = { + path: "/a/b/app.ts", + content: "let z = 1;" + }; + + function fileContentWithComment(file: File) { + return `// some copy right notice +${file.content}`; + } + + function verifyText(service: server.ProjectService, file: string, expected: string) { + const info = service.getScriptInfo(file)!; + const snap = info.getSnapshot(); + // Verified applied in reverse order + assert.equal(snap.getText(0, snap.getLength()), expected, `Text of changed file: ${file}`); + } + + function verifyProjectVersion(project: server.Project, expected: number) { + assert.equal(Number(project.getProjectVersion()), expected); + } + + function verify(applyChangesToOpen: (session: TestSession) => void) { + const host = createServerHost([app, file3, commonFile1, commonFile2, libFile, configFile]); + const session = projectSystem.createSession(host); + session.executeCommandSeq({ + command: protocol.CommandTypes.Open, + arguments: { file: app.path } + }); + const service = session.getProjectService(); + const project = service.configuredProjects.get(configFile.path)!; + assert.isDefined(project); + verifyProjectVersion(project, 1); + session.executeCommandSeq({ + command: protocol.CommandTypes.Open, + arguments: { + file: file3.path, + fileContent: fileContentWithComment(file3) + } + }); + verifyProjectVersion(project, 2); + + // Verify Texts + verifyText(service, commonFile1.path, commonFile1.content); + verifyText(service, commonFile2.path, commonFile2.content); + verifyText(service, app.path, app.content); + verifyText(service, file3.path, fileContentWithComment(file3)); + + // Apply changes + applyChangesToOpen(session); + + // Verify again + verifyProjectVersion(project, 5); + // Open file contents + verifyText(service, commonFile1.path, fileContentWithComment(commonFile1)); + verifyText(service, commonFile2.path, fileContentWithComment(commonFile2)); + verifyText(service, app.path, "let zzz = 10;let zz = 10;let z = 1;"); + verifyText(service, file3.path, file3.content); + } + + it("with applyChangedToOpenFiles request", () => { + verify(session => + session.executeCommandSeq({ + command: protocol.CommandTypes.ApplyChangedToOpenFiles, + arguments: { + openFiles: [ + { + fileName: commonFile1.path, + content: fileContentWithComment(commonFile1) + }, + { + fileName: commonFile2.path, + content: fileContentWithComment(commonFile2) + } + ], + changedFiles: [ + { + fileName: app.path, + changes: [ + { + span: { start: 0, length: 0 }, + newText: "let zzz = 10;" + }, + { + span: { start: 0, length: 0 }, + newText: "let zz = 10;" + } + ] + } + ], + closedFiles: [ + file3.path + ] + } + }) + ); + }); + }); +} From 2258bb2fb7f6a4956ccef86fe4a06be10a254cce Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 25 Feb 2019 14:58:59 -0800 Subject: [PATCH 44/83] Add request applyChangesToOpenFiles --- src/compiler/core.ts | 15 +++++++ src/server/editorServices.ts | 35 ++++++++++++---- src/server/protocol.ts | 27 ++++++++++++ src/server/session.ts | 36 ++++++++++++++-- .../tsserver/applyChangesToOpenFiles.ts | 42 ++++++++++++++++++- .../unittests/tsserver/documentRegistry.ts | 4 +- .../unittests/tsserver/externalProjects.ts | 6 +-- src/testRunner/unittests/tsserver/projects.ts | 19 +++++---- .../reference/api/tsserverlibrary.d.ts | 25 +++++++++++ 9 files changed, 182 insertions(+), 27 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 4bd4801ce8f..112eca82926 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1173,6 +1173,21 @@ namespace ts { }}; } + export function arrayReverseIterator(array: ReadonlyArray): Iterator { + let i = array.length; + return { + next: () => { + if (i === 0) { + return { value: undefined as never, done: true }; + } + else { + i--; + return { value: array[i], done: false }; + } + } + }; + } + /** * Stable sort of an array. Elements equal to each other maintain their relative position in the array. */ diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index e9d97f6e03c..ed51028392e 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -407,6 +407,21 @@ namespace ts.server { } } + /*@internal*/ + export interface OpenFileArguments { + fileName: string; + content?: string; + scriptKind?: protocol.ScriptKindName | ScriptKind; + hasMixedContent?: boolean; + projectRootPath?: string; + } + + /*@internal*/ + export interface ChangeFileArguments { + fileName: string; + changes: Iterator; + } + export class ProjectService { /*@internal*/ @@ -2770,18 +2785,22 @@ namespace ts.server { } /* @internal */ - applyChangesInOpenFiles(openFiles: protocol.ExternalFile[] | undefined, changedFiles: protocol.ChangedOpenFile[] | undefined, closedFiles: string[] | undefined): void { + applyChangesInOpenFiles(openFiles: Iterator | undefined, changedFiles?: Iterator, closedFiles?: string[]): void { if (openFiles) { - for (const file of openFiles) { + while (true) { + const { value: file, done } = openFiles.next(); + if (done) break; const scriptInfo = this.getScriptInfo(file.fileName); Debug.assert(!scriptInfo || !scriptInfo.isScriptOpen(), "Script should not exist and not be open already"); const normalizedPath = scriptInfo ? scriptInfo.fileName : toNormalizedPath(file.fileName); - this.openClientFileWithNormalizedPath(normalizedPath, file.content, tryConvertScriptKindName(file.scriptKind!), file.hasMixedContent); // TODO: GH#18217 + this.openClientFileWithNormalizedPath(normalizedPath, file.content, tryConvertScriptKindName(file.scriptKind!), file.hasMixedContent, file.projectRootPath ? toNormalizedPath(file.projectRootPath) : undefined); // TODO: GH#18217 } } if (changedFiles) { - for (const file of changedFiles) { + while (true) { + const { value: file, done } = changedFiles.next(); + if (done) break; const scriptInfo = this.getScriptInfo(file.fileName)!; Debug.assert(!!scriptInfo); this.applyChangesToFile(scriptInfo, file.changes); @@ -2796,10 +2815,10 @@ namespace ts.server { } /* @internal */ - applyChangesToFile(scriptInfo: ScriptInfo, changes: TextChange[]) { - // apply changes in reverse order - for (let i = changes.length - 1; i >= 0; i--) { - const change = changes[i]; + applyChangesToFile(scriptInfo: ScriptInfo, changes: Iterator) { + while (true) { + const { value: change, done } = changes.next(); + if (done) break; scriptInfo.editContent(change.span.start, change.span.start + change.span.length, change.newText); } } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 1fd8d98b570..3534a991c48 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -92,6 +92,7 @@ namespace ts.server.protocol { SynchronizeProjectList = "synchronizeProjectList", /* @internal */ ApplyChangedToOpenFiles = "applyChangedToOpenFiles", + ApplyChangesToOpenFiles = "applyChangesToOpenFiles", /* @internal */ EncodedSemanticClassificationsFull = "encodedSemanticClassifications-full", /* @internal */ @@ -1543,6 +1544,32 @@ namespace ts.server.protocol { closedFiles?: string[]; } + /** + * Request to synchronize list of open files with the client + */ + export interface ApplyChangesToOpenFilesRequest extends Request { + command: CommandTypes.ApplyChangesToOpenFiles; + arguments: ApplyChangesToOpenFilesRequestArgs; + } + + /** + * Arguments to ApplyChangesToOpenFilesRequest + */ + export interface ApplyChangesToOpenFilesRequestArgs { + /** + * List of newly open files + */ + openFiles?: OpenRequestArgs[]; + /** + * List of open files files that were changes + */ + changedFiles?: FileCodeEdits[]; + /** + * List of files that were closed + */ + closedFiles?: string[]; + } + /** * Request to set compiler options for inferred projects. * External projects are opened / closed explicitly. diff --git a/src/server/session.ts b/src/server/session.ts index b47dfe6b716..42946002bc4 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1654,10 +1654,10 @@ namespace ts.server { const end = scriptInfo.lineOffsetToPosition(args.endLine, args.endOffset); if (start >= 0) { this.changeSeq++; - this.projectService.applyChangesToFile(scriptInfo, [{ + this.projectService.applyChangesToFile(scriptInfo, singleIterator({ span: { start, length: end - start }, newText: args.insertString! // TODO: GH#18217 - }]); + })); } } @@ -2096,9 +2096,39 @@ namespace ts.server { }); return this.requiredResponse(converted); }, + [CommandNames.ApplyChangesToOpenFiles]: (request: protocol.ApplyChangesToOpenFilesRequest) => { + this.changeSeq++; + this.projectService.applyChangesInOpenFiles( + request.arguments.openFiles && mapIterator(arrayIterator(request.arguments.openFiles), file => ({ + fileName: file.file, + content: file.fileContent, + scriptKind: file.scriptKindName, + projectRootPath: file.projectRootPath + })), + request.arguments.changedFiles && mapIterator(arrayIterator(request.arguments.changedFiles), file => ({ + fileName: file.fileName, + changes: mapDefinedIterator(arrayIterator(file.textChanges), change => { + const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(file.fileName)); + const start = scriptInfo.lineOffsetToPosition(change.start.line, change.start.offset); + const end = scriptInfo.lineOffsetToPosition(change.end.line, change.end.offset); + return start >= 0 ? { span: { start, length: end - start }, newText: change.newText } : undefined; + }) + })), + request.arguments.closedFiles + ); + return this.requiredResponse(/*response*/ true); + }, [CommandNames.ApplyChangedToOpenFiles]: (request: protocol.ApplyChangedToOpenFilesRequest) => { this.changeSeq++; - this.projectService.applyChangesInOpenFiles(request.arguments.openFiles, request.arguments.changedFiles!, request.arguments.closedFiles!); // TODO: GH#18217 + this.projectService.applyChangesInOpenFiles( + request.arguments.openFiles && arrayIterator(request.arguments.openFiles), + request.arguments.changedFiles && mapIterator(arrayIterator(request.arguments.changedFiles), file => ({ + fileName: file.fileName, + // apply changes in reverse order + changes: arrayReverseIterator(file.changes) + })), + request.arguments.closedFiles + ); // TODO: report errors return this.requiredResponse(/*response*/ true); }, diff --git a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts index f964e77e0eb..e5dd2e58754 100644 --- a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts +++ b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts @@ -31,7 +31,7 @@ ${file.content}`; function verify(applyChangesToOpen: (session: TestSession) => void) { const host = createServerHost([app, file3, commonFile1, commonFile2, libFile, configFile]); - const session = projectSystem.createSession(host); + const session = createSession(host); session.executeCommandSeq({ command: protocol.CommandTypes.Open, arguments: { file: app.path } @@ -104,5 +104,45 @@ ${file.content}`; }) ); }); + + it("with applyChangesToOpenFiles request", () => { + verify(session => + session.executeCommandSeq({ + command: protocol.CommandTypes.ApplyChangesToOpenFiles, + arguments: { + openFiles: [ + { + file: commonFile1.path, + fileContent: fileContentWithComment(commonFile1) + }, + { + file: commonFile2.path, + fileContent: fileContentWithComment(commonFile2) + } + ], + changedFiles: [ + { + fileName: app.path, + textChanges: [ + { + start: { line: 1, offset: 1 }, + end: { line: 1, offset: 1 }, + newText: "let zz = 10;", + }, + { + start: { line: 1, offset: 1 }, + end: { line: 1, offset: 1 }, + newText: "let zzz = 10;", + } + ] + } + ], + closedFiles: [ + file3.path + ] + } + }) + ); + }); }); } diff --git a/src/testRunner/unittests/tsserver/documentRegistry.ts b/src/testRunner/unittests/tsserver/documentRegistry.ts index 1761e413833..10723300cc0 100644 --- a/src/testRunner/unittests/tsserver/documentRegistry.ts +++ b/src/testRunner/unittests/tsserver/documentRegistry.ts @@ -41,13 +41,13 @@ namespace ts.projectSystem { function changeFileToNotImportModule(service: TestProjectService) { const info = service.getScriptInfo(file.path)!; - service.applyChangesToFile(info, [{ span: { start: 0, length: importModuleContent.length }, newText: "" }]); + service.applyChangesToFile(info, singleIterator({ span: { start: 0, length: importModuleContent.length }, newText: "" })); checkProject(service, /*moduleIsOrphan*/ true); } function changeFileToImportModule(service: TestProjectService) { const info = service.getScriptInfo(file.path)!; - service.applyChangesToFile(info, [{ span: { start: 0, length: 0 }, newText: importModuleContent }]); + service.applyChangesToFile(info, singleIterator({ span: { start: 0, length: 0 }, newText: importModuleContent })); checkProject(service, /*moduleIsOrphan*/ false); } diff --git a/src/testRunner/unittests/tsserver/externalProjects.ts b/src/testRunner/unittests/tsserver/externalProjects.ts index 2055141538a..82c706500d3 100644 --- a/src/testRunner/unittests/tsserver/externalProjects.ts +++ b/src/testRunner/unittests/tsserver/externalProjects.ts @@ -161,7 +161,7 @@ namespace ts.projectSystem { checkNumberOfInferredProjects(projectService, 0); externalFiles[0].content = "let x =1;"; - projectService.applyChangesInOpenFiles(externalFiles, [], []); + projectService.applyChangesInOpenFiles(arrayIterator(externalFiles)); }); it("external project that included config files", () => { @@ -790,9 +790,7 @@ namespace ts.projectSystem { rootFiles: [{ fileName: tsconfig.path }, { fileName: jsFilePath }], options: { allowJs: false } }]); - service.applyChangesInOpenFiles([ - { fileName: jsFilePath, scriptKind: ScriptKind.JS, content: "" } - ], /*changedFiles*/ undefined, /*closedFiles*/ undefined); + service.applyChangesInOpenFiles(singleIterator({ fileName: jsFilePath, scriptKind: ScriptKind.JS, content: "" })); checkNumberOfProjects(service, { configuredProjects: 1, inferredProjects: 1 }); checkProjectActualFiles(configProject, [tsconfig.path]); const inferredProject = service.inferredProjects[0]; diff --git a/src/testRunner/unittests/tsserver/projects.ts b/src/testRunner/unittests/tsserver/projects.ts index 3a648c9189e..72eff18f5ba 100644 --- a/src/testRunner/unittests/tsserver/projects.ts +++ b/src/testRunner/unittests/tsserver/projects.ts @@ -202,7 +202,7 @@ namespace ts.projectSystem { const host = createServerHost([file1, config1]); const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true }); - projectService.applyChangesInOpenFiles([{ fileName: file1.path, content: file1.content }], [], []); + projectService.applyChangesInOpenFiles(singleIterator({ fileName: file1.path, content: file1.content })); checkNumberOfProjects(projectService, { inferredProjects: 1 }); const proj = projectService.inferredProjects[0]; @@ -588,11 +588,11 @@ namespace ts.projectSystem { const host = createServerHost([]); const projectService = createProjectService(host); - projectService.applyChangesInOpenFiles([tsFile], [], []); + projectService.applyChangesInOpenFiles(singleIterator(tsFile)); const projs = projectService.synchronizeProjectList([]); projectService.findProject(projs[0].info!.projectName)!.getLanguageService().getNavigationBarItems(tsFile.fileName); projectService.synchronizeProjectList([projs[0].info!]); - projectService.applyChangesInOpenFiles([jsFile], [], []); + projectService.applyChangesInOpenFiles(singleIterator(jsFile)); }); it("config file is deleted", () => { @@ -696,11 +696,12 @@ namespace ts.projectSystem { checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, config.path]); // Open HTML file - projectService.applyChangesInOpenFiles( - /*openFiles*/[{ fileName: file2.path, hasMixedContent: true, scriptKind: ScriptKind.JS, content: `var hello = "hello";` }], - /*changedFiles*/ undefined, - /*closedFiles*/ undefined); - + projectService.applyChangesInOpenFiles(singleIterator({ + fileName: file2.path, + hasMixedContent: true, + scriptKind: ScriptKind.JS, + content: `var hello = "hello";` + })); // Now HTML file is included in the project checkNumberOfProjects(projectService, { configuredProjects: 1 }); checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, config.path]); @@ -852,7 +853,7 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { inferredProjects: 1 }); projectService.applyChangesInOpenFiles( /*openFiles*/ undefined, - /*changedFiles*/[{ fileName: file1.path, changes: [{ span: createTextSpan(0, file1.path.length), newText: "let y = 1" }] }], + /*changedFiles*/singleIterator({ fileName: file1.path, changes: singleIterator({ span: createTextSpan(0, file1.path.length), newText: "let y = 1" }) }), /*closedFiles*/ undefined); checkNumberOfProjects(projectService, { inferredProjects: 1 }); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index fab6f905efc..eb522125ddd 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5720,6 +5720,7 @@ declare namespace ts.server.protocol { OpenExternalProject = "openExternalProject", OpenExternalProjects = "openExternalProjects", CloseExternalProject = "closeExternalProject", + ApplyChangesToOpenFiles = "applyChangesToOpenFiles", GetOutliningSpans = "getOutliningSpans", TodoComments = "todoComments", Indentation = "indentation", @@ -6788,6 +6789,30 @@ declare namespace ts.server.protocol { */ interface CloseExternalProjectResponse extends Response { } + /** + * Request to synchronize list of open files with the client + */ + interface ApplyChangesToOpenFilesRequest extends Request { + command: CommandTypes.ApplyChangesToOpenFiles; + arguments: ApplyChangesToOpenFilesRequestArgs; + } + /** + * Arguments to ApplyChangesToOpenFilesRequest + */ + interface ApplyChangesToOpenFilesRequestArgs { + /** + * List of newly open files + */ + openFiles?: OpenRequestArgs[]; + /** + * List of open files files that were changes + */ + changedFiles?: FileCodeEdits[]; + /** + * List of files that were closed + */ + closedFiles?: string[]; + } /** * Request to set compiler options for inferred projects. * External projects are opened / closed explicitly. From e6068f405bdb371f5edfdeb61e910b4abcd1ee81 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 26 Feb 2019 11:43:10 -0800 Subject: [PATCH 45/83] Make applyChangesToOpenFiles efficient to handle batch file opens, close and changes before updating projects Fixes #29667 --- src/server/editorServices.ts | 101 +++++++++++++----- .../tsserver/applyChangesToOpenFiles.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 4 + 3 files changed, 80 insertions(+), 27 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index ed51028392e..10939b68114 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1143,11 +1143,22 @@ namespace ts.server { return project; } + private assignOrphanScriptInfosToInferredProject() { + // collect orphaned files and assign them to inferred project just like we treat open of a file + this.openFiles.forEach((projectRootPath, path) => { + const info = this.getScriptInfoForPath(path as Path)!; + // collect all orphaned script infos from open files + if (info.isOrphan()) { + this.assignOrphanScriptInfoToInferredProject(info, projectRootPath); + } + }); + } + /** * Remove this file from the set of open, non-configured files. * @param info The file that has been closed or newly configured */ - private closeOpenFile(info: ScriptInfo): void { + private closeOpenFile(info: ScriptInfo, skipAssignOrphanScriptInfosToInferredProject?: true) { // Closing file should trigger re-reading the file content from disk. This is // because the user may chose to discard the buffer content before saving // to the disk, and the server's version of the file can be out of sync. @@ -1191,15 +1202,8 @@ namespace ts.server { this.openFiles.delete(info.path); - if (ensureProjectsForOpenFiles) { - // collect orphaned files and assign them to inferred project just like we treat open of a file - this.openFiles.forEach((projectRootPath, path) => { - const info = this.getScriptInfoForPath(path as Path)!; - // collect all orphaned script infos from open files - if (info.isOrphan()) { - this.assignOrphanScriptInfoToInferredProject(info, projectRootPath); - } - }); + if (!skipAssignOrphanScriptInfosToInferredProject && ensureProjectsForOpenFiles) { + this.assignOrphanScriptInfosToInferredProject(); } // Cleanup script infos that arent part of any project (eg. those could be closed script infos not referenced by any project) @@ -1214,6 +1218,8 @@ namespace ts.server { else { this.handleDeletedFile(info); } + + return ensureProjectsForOpenFiles; } private deleteScriptInfo(info: ScriptInfo) { @@ -2585,20 +2591,22 @@ namespace ts.server { }); } - openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult { + private getOrCreateOpenScriptInfo(fileName: NormalizedPath, fileContent: string | undefined, scriptKind: ScriptKind | undefined, hasMixedContent: boolean | undefined, projectRootPath: NormalizedPath | undefined) { + const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, projectRootPath ? this.getNormalizedAbsolutePath(projectRootPath) : this.currentDirectory, fileContent, scriptKind, hasMixedContent)!; // TODO: GH#18217 + this.openFiles.set(info.path, projectRootPath); + return info; + } + + private assignProjectToOpenedScriptInfo(info: ScriptInfo): OpenConfiguredProjectResult { let configFileName: NormalizedPath | undefined; let configFileErrors: ReadonlyArray | undefined; - - const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, projectRootPath ? this.getNormalizedAbsolutePath(projectRootPath) : this.currentDirectory, fileContent, scriptKind, hasMixedContent)!; // TODO: GH#18217 - - this.openFiles.set(info.path, projectRootPath); let project: ConfiguredProject | ExternalProject | undefined = this.findExternalProjectContainingOpenScriptInfo(info); if (!project && !this.syntaxOnly) { // Checking syntaxOnly is an optimization configFileName = this.getConfigFileNameForFile(info); if (configFileName) { project = this.findConfiguredProjectByProjectName(configFileName); if (!project) { - project = this.createLoadAndUpdateConfiguredProject(configFileName, `Creating possible configured project for ${fileName} to open`); + project = this.createLoadAndUpdateConfiguredProject(configFileName, `Creating possible configured project for ${info.fileName} to open`); // Send the event only if the project got created as part of this open request and info is part of the project if (info.isOrphan()) { // Since the file isnt part of configured project, do not send config file info @@ -2606,7 +2614,7 @@ namespace ts.server { } else { configFileErrors = project.getAllProjectErrors(); - this.sendConfigFileDiagEvent(project, fileName); + this.sendConfigFileDiagEvent(project, info.fileName); } } else { @@ -2628,10 +2636,14 @@ namespace ts.server { // At this point if file is part of any any configured or external project, then it would be present in the containing projects // So if it still doesnt have any containing projects, it needs to be part of inferred project if (info.isOrphan()) { - this.assignOrphanScriptInfoToInferredProject(info, projectRootPath); + Debug.assert(this.openFiles.has(info.path)); + this.assignOrphanScriptInfoToInferredProject(info, this.openFiles.get(info.path)); } Debug.assert(!info.isOrphan()); + return { configFileName, configFileErrors }; + } + private cleanupAfterOpeningFile() { // This was postponed from closeOpenFile to after opening next file, // so that we can reuse the project if we need to right away this.removeOrphanConfiguredProjects(); @@ -2651,9 +2663,14 @@ namespace ts.server { this.removeOrphanScriptInfos(); this.printProjects(); + } + openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult { + const info = this.getOrCreateOpenScriptInfo(fileName, fileContent, scriptKind, hasMixedContent, projectRootPath); + const result = this.assignProjectToOpenedScriptInfo(info); + this.cleanupAfterOpeningFile(); this.telemetryOnOpenFile(info); - return { configFileName, configFileErrors }; + return result; } private removeOrphanConfiguredProjects() { @@ -2760,12 +2777,16 @@ namespace ts.server { * Close file whose contents is managed by the client * @param filename is absolute pathname */ - closeClientFile(uncheckedFileName: string) { + closeClientFile(uncheckedFileName: string): void; + /*@internal*/ + closeClientFile(uncheckedFileName: string, skipAssignOrphanScriptInfosToInferredProject: true): boolean; + closeClientFile(uncheckedFileName: string, skipAssignOrphanScriptInfosToInferredProject?: true) { const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName)); - if (info) { - this.closeOpenFile(info); + const result = info ? this.closeOpenFile(info, skipAssignOrphanScriptInfosToInferredProject) : false; + if (!skipAssignOrphanScriptInfosToInferredProject) { + this.printProjects(); } - this.printProjects(); + return result; } private collectChanges(lastKnownProjectVersions: protocol.ProjectVersionInfo[], currentProjects: Project[], result: ProjectFilesWithTSDiagnostics[]): void { @@ -2786,14 +2807,23 @@ namespace ts.server { /* @internal */ applyChangesInOpenFiles(openFiles: Iterator | undefined, changedFiles?: Iterator, closedFiles?: string[]): void { + let openScriptInfos: ScriptInfo[] | undefined; + let assignOrphanScriptInfosToInferredProject = false; if (openFiles) { while (true) { const { value: file, done } = openFiles.next(); if (done) break; const scriptInfo = this.getScriptInfo(file.fileName); Debug.assert(!scriptInfo || !scriptInfo.isScriptOpen(), "Script should not exist and not be open already"); - const normalizedPath = scriptInfo ? scriptInfo.fileName : toNormalizedPath(file.fileName); - this.openClientFileWithNormalizedPath(normalizedPath, file.content, tryConvertScriptKindName(file.scriptKind!), file.hasMixedContent, file.projectRootPath ? toNormalizedPath(file.projectRootPath) : undefined); // TODO: GH#18217 + // Create script infos so we have the new content for all the open files before we do any updates to projects + const info = this.getOrCreateOpenScriptInfo( + scriptInfo ? scriptInfo.fileName : toNormalizedPath(file.fileName), + file.content, + tryConvertScriptKindName(file.scriptKind!), + file.hasMixedContent, + file.projectRootPath ? toNormalizedPath(file.projectRootPath) : undefined + ); + (openScriptInfos || (openScriptInfos = [])).push(info); } } @@ -2803,15 +2833,34 @@ namespace ts.server { if (done) break; const scriptInfo = this.getScriptInfo(file.fileName)!; Debug.assert(!!scriptInfo); + // Make edits to script infos and marks containing project as dirty this.applyChangesToFile(scriptInfo, file.changes); } } if (closedFiles) { for (const file of closedFiles) { - this.closeClientFile(file); + // Close files, but dont assign projects to orphan open script infos, that part comes later + assignOrphanScriptInfosToInferredProject = this.closeClientFile(file, /*skipAssignOrphanScriptInfosToInferredProject*/ true) || assignOrphanScriptInfosToInferredProject; } } + + // All the script infos now exist, so ok to go update projects for open files + if (openScriptInfos) { + openScriptInfos.forEach(info => this.assignProjectToOpenedScriptInfo(info)); + } + + // While closing files there could be open files that needed assigning new inferred projects, do it now + if (assignOrphanScriptInfosToInferredProject) { + this.assignOrphanScriptInfosToInferredProject(); + } + + // Cleanup projects + this.cleanupAfterOpeningFile(); + + // Telemetry + forEach(openScriptInfos, info => this.telemetryOnOpenFile(info)); + this.printProjects(); } /* @internal */ diff --git a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts index e5dd2e58754..3c0da365bb5 100644 --- a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts +++ b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts @@ -59,7 +59,7 @@ ${file.content}`; applyChangesToOpen(session); // Verify again - verifyProjectVersion(project, 5); + verifyProjectVersion(project, 3); // Open file contents verifyText(service, commonFile1.path, fileContentWithComment(commonFile1)); verifyText(service, commonFile2.path, fileContentWithComment(commonFile2)); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index eb522125ddd..f3fdd5158b4 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -8652,6 +8652,7 @@ declare namespace ts.server { */ private onConfigFileChangeForOpenScriptInfo; private removeProject; + private assignOrphanScriptInfosToInferredProject; /** * Remove this file from the set of open, non-configured files. * @param info The file that has been closed or newly configured @@ -8770,6 +8771,9 @@ declare namespace ts.server { */ openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: string): OpenConfiguredProjectResult; private findExternalProjectContainingOpenScriptInfo; + private getOrCreateOpenScriptInfo; + private assignProjectToOpenedScriptInfo; + private cleanupAfterOpeningFile; openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult; private removeOrphanConfiguredProjects; private removeOrphanScriptInfos; From 006fe14bcc670e2c47afce6cd69c26f2467b6f03 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 26 Feb 2019 16:22:08 -0800 Subject: [PATCH 46/83] refactor utilities --- src/services/utilities.ts | 65 ++++++++++----------------------------- 1 file changed, 17 insertions(+), 48 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 0536e6f34ea..afa14f62a1d 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1753,33 +1753,23 @@ namespace ts { /** * Sets EmitFlags to suppress leading and trailing trivia on the node. */ - export function suppressLeadingAndTrailingTrivia(node: Node, recursive = true) { - suppressLeadingTrivia(node, recursive); - suppressTrailingTrivia(node, recursive); + export function suppressLeadingAndTrailingTrivia(node: Node) { + suppressLeadingTrivia(node); + suppressTrailingTrivia(node); } /** * Sets EmitFlags to suppress leading trivia on the node. */ - export function suppressLeadingTrivia(node: Node, recursive = true) { - if (recursive) { - addEmitFlagsRecursively(node, EmitFlags.NoLeadingComments, getFirstChild); - } - else { - addEmitFlags(node, EmitFlags.NoLeadingComments); - } + export function suppressLeadingTrivia(node: Node) { + addEmitFlagsRecursively(node, EmitFlags.NoLeadingComments, getFirstChild); } /** * Sets EmitFlags to suppress trailing trivia on the node. */ - export function suppressTrailingTrivia(node: Node, recursive = true) { - if (recursive) { - addEmitFlagsRecursively(node, EmitFlags.NoTrailingComments, getLastChild); - } - else { - addEmitFlags(node, EmitFlags.NoTrailingComments); - } + export function suppressTrailingTrivia(node: Node) { + addEmitFlagsRecursively(node, EmitFlags.NoTrailingComments, getLastChild); } function addEmitFlagsRecursively(node: Node, flag: EmitFlags, getChild: (n: Node) => Node | undefined) { @@ -1832,35 +1822,12 @@ namespace ts { } export function copyLeadingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) { - forEachLeadingCommentRange(sourceFile.text, sourceNode.pos, (pos, end, kind, htnl) => { - if (kind === SyntaxKind.MultiLineCommentTrivia) { - // Remove leading /* - pos += 2; - // Remove trailing */ - end -= 2; - } - else { - // Remove leading // - pos += 2; - } - addSyntheticLeadingComment(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl); - }); + forEachLeadingCommentRange(sourceFile.text, sourceNode.pos, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticLeadingComment)); } + export function copyTrailingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) { - forEachTrailingCommentRange(sourceFile.text, sourceNode.end, (pos, end, kind, htnl) => { - if (kind === SyntaxKind.MultiLineCommentTrivia) { - // Remove leading /* - pos += 2; - // Remove trailing */ - end -= 2; - } - else { - // Remove leading // - pos += 2; - } - addSyntheticTrailingComment(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl); - }); + forEachTrailingCommentRange(sourceFile.text, sourceNode.end, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticTrailingComment)); } /** @@ -1871,7 +1838,11 @@ namespace ts { * The comment refers to `a` but belongs to the `(` token, but we might want to copy it. */ export function copyTrailingAsLeadingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) { - forEachTrailingCommentRange(sourceFile.text, sourceNode.pos, (pos, end, kind, htnl) => { + forEachTrailingCommentRange(sourceFile.text, sourceNode.pos, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticLeadingComment)); + } + + function getAddCommentsFunction(targetNode: Node, sourceFile: SourceFile, commentKind: CommentKind | undefined, hasTrailingNewLine: boolean | undefined, cb: (node: Node, kind: CommentKind, text: string, hasTrailingNewLine?: boolean) => void) { + return (pos: number, end: number, kind: CommentKind, htnl: boolean) => { if (kind === SyntaxKind.MultiLineCommentTrivia) { // Remove leading /* pos += 2; @@ -1882,12 +1853,10 @@ namespace ts { // Remove leading // pos += 2; } - addSyntheticLeadingComment(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl); - }); + cb(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl); + }; } - - function indexInTextChange(change: string, name: string): number { if (startsWith(change, name)) return 0; // Add a " " to avoid references inside words From 03377f70b776d5e1279a70d2e75b45a45483ba0d Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 27 Feb 2019 14:07:15 -0800 Subject: [PATCH 47/83] Apply changes in reverse order even in new API to match behaviour with internal api --- src/server/session.ts | 2 +- src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index 42946002bc4..af84ffff48a 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -2107,7 +2107,7 @@ namespace ts.server { })), request.arguments.changedFiles && mapIterator(arrayIterator(request.arguments.changedFiles), file => ({ fileName: file.fileName, - changes: mapDefinedIterator(arrayIterator(file.textChanges), change => { + changes: mapDefinedIterator(arrayReverseIterator(file.textChanges), change => { const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(file.fileName)); const start = scriptInfo.lineOffsetToPosition(change.start.line, change.start.offset); const end = scriptInfo.lineOffsetToPosition(change.end.line, change.end.offset); diff --git a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts index 3c0da365bb5..68c3839e789 100644 --- a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts +++ b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts @@ -127,12 +127,12 @@ ${file.content}`; { start: { line: 1, offset: 1 }, end: { line: 1, offset: 1 }, - newText: "let zz = 10;", + newText: "let zzz = 10;", }, { start: { line: 1, offset: 1 }, end: { line: 1, offset: 1 }, - newText: "let zzz = 10;", + newText: "let zz = 10;", } ] } From 54c7996ff511e7a95c2184111abe7c611041780f Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 27 Feb 2019 16:19:01 -0800 Subject: [PATCH 48/83] refactor getGroupReferences --- .../refactors/convertToNamedParameters.ts | 170 +++++++++--------- src/services/utilities.ts | 12 ++ 2 files changed, 96 insertions(+), 86 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index fa285619dab..f0873a56baf 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -4,14 +4,14 @@ namespace ts.refactor.convertToNamedParameters { const refactorDescription = "Convert to named parameters"; const actionNameNamedParameters = "Convert to named parameters"; const actionDescriptionNamedParameters = "Convert to named parameters"; - const minimumParameterLength = 1; + const minimumParameterLength = 2; registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): ReadonlyArray { const { file, startPosition } = context; const isJSFile = isSourceFileJS(file); - if (isJSFile) return emptyArray; + if (isJSFile) return emptyArray; // TODO: GH#30113 const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker()); if (!functionDeclaration) return emptyArray; @@ -37,7 +37,7 @@ namespace ts.refactor.convertToNamedParameters { return { renameFilename: undefined, renameLocation: undefined, edits }; } - return { edits: [] }; + return { edits: [] }; // TODO: GH#30113 } function doChange(sourceFile: SourceFile, program: Program, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration, groupedReferences: GroupedReferences): void { @@ -55,7 +55,7 @@ namespace ts.refactor.convertToNamedParameters { }); - const functionCalls = deduplicate(groupedReferences.functionCalls, (a, b) => a === b); + const functionCalls = deduplicate(groupedReferences.functionCalls, equateValues); forEach(functionCalls, call => { if (call.arguments && call.arguments.length) { const newArgument = getSynthesizedDeepClone(createNewArgument(functionDeclaration, call.arguments), /*includeTrivia*/ true); @@ -69,60 +69,57 @@ namespace ts.refactor.convertToNamedParameters { } function getGroupedReferences(functionDeclaration: ValidFunctionDeclaration, program: Program, cancellationToken: CancellationToken): GroupedReferences { - const names = getDeclarationNames(functionDeclaration); - const references = flatMap(names, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); - let groupedReferences = groupReferences(references); + const functionNames = getFunctionNames(functionDeclaration); + const classNames = isConstructorDeclaration(functionDeclaration) ? getClassNames(functionDeclaration) : []; + const names = deduplicate([...functionNames, ...classNames], equateValues); + const checker = program.getTypeChecker(); - // if the refactored function is a constructor, we must also go through the references to its class - if (isConstructorDeclaration(functionDeclaration)) { - const className = getClassName(functionDeclaration); - groupedReferences = groupClassReferences(groupedReferences, className); + const references = flatMap(names, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); + const isConstructor = isConstructorDeclaration(functionDeclaration); + const groupedReferences = groupReferences(references, isConstructor); + + if (!every(groupedReferences.declarations, decl => contains(names, decl))) { + groupedReferences.valid = false; } - validateReferences(groupedReferences); return groupedReferences; - function getClassName(constructorDeclaration: ValidConstructor): Identifier { - switch (constructorDeclaration.parent.kind) { - case SyntaxKind.ClassDeclaration: - return constructorDeclaration.parent.name; - case SyntaxKind.ClassExpression: - return constructorDeclaration.parent.parent.name; - } - } - - function groupReferences(referenceEntries: ReadonlyArray | undefined): GroupedReferences { - const groupedReferences: GroupedReferences = { functionCalls: [], declarations: [], unhandled: [], valid: true }; - - forEach(referenceEntries, (entry) => { - const decl = entryToDeclaration(entry); - if (decl) { - groupedReferences.declarations.push(decl); - return; - } - - const call = entryToFunctionCall(entry); - if (call) { - groupedReferences.functionCalls.push(call); - return; - } - - groupedReferences.unhandled.push(entry); - }); - return groupedReferences; - } - - function groupClassReferences(groupedReferences: GroupedReferences, className: Identifier): GroupedReferences { + function groupReferences(referenceEntries: ReadonlyArray, isConstructor: boolean): GroupedReferences { const classReferences: ClassReferences = { accessExpressions: [], typeUsages: [] }; - const unhandledEntries = groupedReferences.unhandled; - const newUnhandledEntries: FindAllReferences.Entry[] = []; + const groupedReferences: GroupedReferences = { functionCalls: [], declarations: [], classReferences, valid: true }; + const functionSymbols = map(functionNames, checker.getSymbolAtLocation); + const classSymbols = map(classNames, checker.getSymbolAtLocation); + + for (const entry of referenceEntries) { + if (entry.kind !== FindAllReferences.EntryKind.Node) { + groupedReferences.valid = false; + continue; + } + if (contains(functionSymbols, checker.getSymbolAtLocation(entry.node), symbolComparer)) { + const decl = entryToDeclaration(entry); + if (decl) { + groupedReferences.declarations.push(decl); + continue; + } + + const call = entryToFunctionCall(entry); + if (call) { + groupedReferences.functionCalls.push(call); + continue; + } + } + // if the refactored function is a constructor, we must also check if the references to its class are valid + if (isConstructor && contains(classSymbols, checker.getSymbolAtLocation(entry.node), symbolComparer)) { + const decl = entryToDeclaration(entry); + if (decl) { + groupedReferences.declarations.push(decl); + continue; + } - forEach(unhandledEntries, (entry) => { - if (entry.kind === FindAllReferences.EntryKind.Node && entry.node.symbol === className.symbol) { const accessExpression = entryToAccessExpression(entry); if (accessExpression) { classReferences.accessExpressions.push(accessExpression); - return; + continue; } // Only class declarations are allowed to be used as a type (in a heritage clause), @@ -131,27 +128,29 @@ namespace ts.refactor.convertToNamedParameters { const type = entryToType(entry); if (type) { classReferences.typeUsages.push(type); - return; + continue; } } } - newUnhandledEntries.push(entry); - }); - - return { ...groupedReferences, classReferences, unhandled: newUnhandledEntries }; - } - - function validateReferences(groupedReferences: GroupedReferences): void { - if (groupedReferences.unhandled.length > 0) { - groupedReferences.valid = false; - } - if (!every(groupedReferences.declarations, decl => contains(names, decl))) { groupedReferences.valid = false; } + + return groupedReferences; } - function entryToFunctionCall(entry: FindAllReferences.Entry): CallExpression | NewExpression | undefined { - if (entry.kind === FindAllReferences.EntryKind.Node && entry.node.parent) { + function symbolComparer(a: Symbol, b: Symbol): boolean { + return getSymbolTarget(a) === getSymbolTarget(b); + } + + function entryToDeclaration(entry: FindAllReferences.NodeEntry): Node | undefined { + if (isDeclaration(entry.node.parent)) { + return entry.node; + } + return undefined; + } + + function entryToFunctionCall(entry: FindAllReferences.NodeEntry): CallExpression | NewExpression | undefined { + if (entry.node.parent) { const functionReference = entry.node; const parent = functionReference.parent; switch (parent.kind) { @@ -194,15 +193,8 @@ namespace ts.refactor.convertToNamedParameters { return undefined; } - function entryToDeclaration(entry: FindAllReferences.Entry): Node | undefined { - if (entry.kind === FindAllReferences.EntryKind.Node && contains(names, entry.node)) { - return entry.node; - } - return undefined; - } - - function entryToAccessExpression(entry: FindAllReferences.Entry): ElementAccessExpression | PropertyAccessExpression | undefined { - if (entry.kind === FindAllReferences.EntryKind.Node && entry.node.parent) { + function entryToAccessExpression(entry: FindAllReferences.NodeEntry): ElementAccessExpression | PropertyAccessExpression | undefined { + if (entry.node.parent) { const reference = entry.node; const parent = reference.parent; switch (parent.kind) { @@ -263,7 +255,7 @@ namespace ts.refactor.convertToNamedParameters { return false; function isValidParameterNodeArray(parameters: NodeArray): parameters is ValidParameterNodeArray { - return getRefactorableParametersLength(parameters) > minimumParameterLength && every(parameters, isValidParameterDeclaration); + return getRefactorableParametersLength(parameters) >= minimumParameterLength && every(parameters, isValidParameterDeclaration); } function isValidParameterDeclaration(paramDeclaration: ParameterDeclaration): paramDeclaration is ValidParameterDeclaration { @@ -271,7 +263,7 @@ namespace ts.refactor.convertToNamedParameters { } function isValidVariableDeclaration(node: Node): node is ValidVariableDeclaration { - return isVariableDeclaration(node) && isVarConst(node) && isIdentifier(node.name) && !node.type; + return isVariableDeclaration(node) && isVarConst(node) && isIdentifier(node.name) && !node.type; // TODO: GH#30113 } } @@ -430,25 +422,32 @@ namespace ts.refactor.convertToNamedParameters { return getTextOfIdentifierOrLiteral(paramDeclaration.name); } - function getDeclarationNames(functionDeclaration: ValidFunctionDeclaration): Node[] { + function getClassNames(constructorDeclaration: ValidConstructor): Identifier[] { + switch (constructorDeclaration.parent.kind) { + case SyntaxKind.ClassDeclaration: + const classDeclaration = constructorDeclaration.parent; + return [classDeclaration.name]; + case SyntaxKind.ClassExpression: + const classExpression = constructorDeclaration.parent; + const variableDeclaration = constructorDeclaration.parent.parent; + const className = classExpression.name; + if (className) return [className, variableDeclaration.name]; + return [variableDeclaration.name]; + } + } + + function getFunctionNames(functionDeclaration: ValidFunctionDeclaration): Node[] { switch (functionDeclaration.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: return [functionDeclaration.name]; case SyntaxKind.Constructor: const ctrKeyword = findChildOfKind(functionDeclaration, SyntaxKind.ConstructorKeyword, functionDeclaration.getSourceFile())!; - switch (functionDeclaration.parent.kind) { - case SyntaxKind.ClassDeclaration: - const classDeclaration = functionDeclaration.parent; - return [classDeclaration.name, ctrKeyword]; - case SyntaxKind.ClassExpression: - const classExpression = functionDeclaration.parent; - const variableDeclaration = functionDeclaration.parent.parent; - const className = classExpression.name; - if (className) return [className, ctrKeyword, variableDeclaration.name]; - return [ctrKeyword, variableDeclaration.name]; - default: return Debug.assertNever(functionDeclaration.parent); + if (functionDeclaration.parent.kind === SyntaxKind.ClassExpression) { + const variableDeclaration = functionDeclaration.parent.parent; + return [variableDeclaration.name, ctrKeyword]; } + return [ctrKeyword]; case SyntaxKind.ArrowFunction: return [functionDeclaration.parent.name]; case SyntaxKind.FunctionExpression: @@ -500,7 +499,6 @@ namespace ts.refactor.convertToNamedParameters { functionCalls: (CallExpression | NewExpression)[]; declarations: Node[]; classReferences?: ClassReferences; - unhandled: FindAllReferences.Entry[]; valid: boolean; } interface ClassReferences { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index afa14f62a1d..004135a526e 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1664,6 +1664,18 @@ namespace ts { return ensureScriptKind(fileName, host && host.getScriptKind && host.getScriptKind(fileName)); } + export function getSymbolTarget(symbol: Symbol): Symbol { + let next: Symbol = symbol; + while (isTransientSymbol(next) && next.target) { + next = next.target; + } + return next; + } + + function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol { + return (symbol.flags & SymbolFlags.Transient) !== 0; + } + export function getUniqueSymbolId(symbol: Symbol, checker: TypeChecker) { return getSymbolId(skipAlias(symbol, checker)); } From 7fd6868f8bc59616125d9a2a1453ba7328b23b24 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 27 Feb 2019 16:42:54 -0800 Subject: [PATCH 49/83] minor refactors to convertToNamedParameters --- .../refactors/convertToNamedParameters.ts | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index f0873a56baf..102001a46a8 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -40,7 +40,13 @@ namespace ts.refactor.convertToNamedParameters { return { edits: [] }; // TODO: GH#30113 } - function doChange(sourceFile: SourceFile, program: Program, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration, groupedReferences: GroupedReferences): void { + function doChange( + sourceFile: SourceFile, + program: Program, + host: LanguageServiceHost, + changes: textChanges.ChangeTracker, + functionDeclaration: ValidFunctionDeclaration, + groupedReferences: GroupedReferences): void { const newParamDeclaration = map(createNewParameters(functionDeclaration, program, host), param => getSynthesizedDeepClone(param)); changes.replaceNodeRangeWithNodes( sourceFile, @@ -74,7 +80,7 @@ namespace ts.refactor.convertToNamedParameters { const names = deduplicate([...functionNames, ...classNames], equateValues); const checker = program.getTypeChecker(); - const references = flatMap(names, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); + const references = flatMap(names, /*mapfn*/ name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); const isConstructor = isConstructorDeclaration(functionDeclaration); const groupedReferences = groupReferences(references, isConstructor); @@ -231,8 +237,12 @@ namespace ts.refactor.convertToNamedParameters { function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined { const node = getTokenAtPosition(file, startPosition); const functionDeclaration = getContainingFunction(node); - if (!functionDeclaration || !isValidFunctionDeclaration(functionDeclaration, checker) || !rangeContainsRange(functionDeclaration, node) || (functionDeclaration.body && rangeContainsRange(functionDeclaration.body, node))) return undefined; - return functionDeclaration; + if (functionDeclaration + && isValidFunctionDeclaration(functionDeclaration, checker) + && rangeContainsRange(functionDeclaration, node) + && !(functionDeclaration.body && rangeContainsRange(functionDeclaration.body, node))) return functionDeclaration; + + return undefined; } function isValidFunctionDeclaration(functionDeclaration: SignatureDeclaration, checker: TypeChecker): functionDeclaration is ValidFunctionDeclaration { @@ -311,10 +321,11 @@ namespace ts.refactor.convertToNamedParameters { const bindingElements = map(refactorableParameters, createBindingElementFromParameterDeclaration); const objectParameterName = createObjectBindingPattern(bindingElements); const objectParameterType = createParameterTypeNode(refactorableParameters); + const checker = program.getTypeChecker(); let objectInitializer: Expression | undefined; // If every parameter in the original function was optional, add an empty object initializer to the new object parameter - if (every(refactorableParameters, param => !!param.initializer || !!param.questionToken)) { + if (every(refactorableParameters, checker.isOptionalParameter)) { objectInitializer = createObjectLiteral(); } @@ -339,9 +350,9 @@ namespace ts.refactor.convertToNamedParameters { suppressLeadingAndTrailingTrivia(newThisParameter.name); copyComments(thisParameter.name, newThisParameter.name); - if (thisParameter.type && newThisParameter.type) { - suppressLeadingAndTrailingTrivia(newThisParameter.type); - copyComments(thisParameter.type, newThisParameter.type); + if (thisParameter.type) { + suppressLeadingAndTrailingTrivia(newThisParameter.type!); + copyComments(thisParameter.type, newThisParameter.type!); } return createNodeArray([newThisParameter, objectParameter]); @@ -453,6 +464,8 @@ namespace ts.refactor.convertToNamedParameters { case SyntaxKind.FunctionExpression: if (functionDeclaration.name) return [functionDeclaration.name, functionDeclaration.parent.name]; return [functionDeclaration.parent.name]; + default: + return Debug.assertNever(functionDeclaration); } } From b44bfaaaa8edf88e83779e1f0399a8518dfff389 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 28 Feb 2019 06:15:26 -0800 Subject: [PATCH 50/83] Convert CheckMode to a flags style enum --- src/compiler/checker.ts | 43 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 002fdecc0cd..440dacc7597 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -681,10 +681,10 @@ namespace ts { } const enum CheckMode { - Normal = 0, // Normal type checking - SkipContextSensitive = 1, // Skip context sensitive function expressions - Inferential = 2, // Inferential typing - Contextual = 3, // Normal type checking informed by a contextual type, therefore not cacheable + Normal = 0, // Normal type checking + Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable + Inferential = 1 << 1, // Inferential typing + SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions } const enum CallbackCheck { @@ -19985,10 +19985,9 @@ namespace ts { function inferJsxTypeArguments(node: JsxOpeningLikeElement, signature: Signature, excludeArgument: ReadonlyArray | undefined, context: InferenceContext): Type[] { const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); - - const checkAttrType = checkExpressionWithContextualType(node.attributes, paramType, excludeArgument && excludeArgument[0] !== undefined ? identityMapper : context); + const checkMode = excludeArgument && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; + const checkAttrType = checkExpressionWithContextualType(node.attributes, paramType, context, checkMode); inferTypes(context.inferences, checkAttrType, paramType); - return getInferredTypes(context); } @@ -20054,8 +20053,8 @@ namespace ts { const paramType = getTypeAtPosition(signature, i); // For context sensitive arguments we pass the identityMapper, which is a signal to treat all // context sensitive function expressions as wildcards - const mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : context; - const argType = checkExpressionWithContextualType(arg, paramType, mapper); + const checkMode = excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0; + const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); inferTypes(context.inferences, argType, paramType); } } @@ -20083,7 +20082,7 @@ namespace ts { // and the argument are ...x forms. return arg.kind === SyntaxKind.SyntheticExpression ? createArrayType((arg).type) : - getArrayifiedType(checkExpressionWithContextualType((arg).expression, restType, context)); + getArrayifiedType(checkExpressionWithContextualType((arg).expression, restType, context, CheckMode.Normal)); } } const contextualType = getIndexTypeOfType(restType, IndexKind.Number) || anyType; @@ -20091,7 +20090,7 @@ namespace ts { const types = []; let spreadIndex = -1; for (let i = index; i < argCount; i++) { - const argType = checkExpressionWithContextualType(args[i], contextualType, context); + const argType = checkExpressionWithContextualType(args[i], contextualType, context, CheckMode.Normal); if (spreadIndex < 0 && isSpreadArgument(args[i])) { spreadIndex = i - index; } @@ -20156,7 +20155,8 @@ namespace ts { // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, // can be specified by users through attributes property. const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); - const attributesType = checkExpressionWithContextualType(node.attributes, paramType, excludeArgument && excludeArgument[0] ? identityMapper : undefined); + const checkMode = excludeArgument && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; + const attributesType = checkExpressionWithContextualType(node.attributes, paramType, undefined, checkMode); return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes); } @@ -20190,7 +20190,8 @@ namespace ts { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { const paramType = getTypeAtPosition(signature, i); - const argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); + const checkMode = excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0; + const argType = checkExpressionWithContextualType(arg, paramType, undefined, checkMode); // If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter), // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). @@ -21145,7 +21146,7 @@ namespace ts { if (isJsxIntrinsicIdentifier(node.tagName)) { const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); const fakeSignature = createSignatureForJSXIntrinsic(node, result); - checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined), result, node.tagName, node.attributes); + checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined, CheckMode.Normal), result, node.tagName, node.attributes); return fakeSignature; } const exprTypes = checkExpression(node.tagName); @@ -22086,7 +22087,7 @@ namespace ts { checkNodeDeferred(node); // The identityMapper object is used to indicate that function expressions are wildcards - if (checkMode === CheckMode.SkipContextSensitive && isContextSensitive(node)) { + if (checkMode && checkMode & CheckMode.SkipContextSensitive && isContextSensitive(node)) { // Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage if (!getEffectiveReturnTypeNode(node) && hasContextSensitiveReturnExpression(node)) { const links = getNodeLinks(node); @@ -22126,7 +22127,7 @@ namespace ts { const signature = getSignaturesOfType(type, SignatureKind.Call)[0]; if (isContextSensitive(node)) { const contextualMapper = getContextualMapper(node); - if (checkMode === CheckMode.Inferential) { + if (checkMode && checkMode & CheckMode.Inferential) { inferFromAnnotatedParameters(signature, contextualSignature, contextualMapper); } const instantiatedContextualSignature = contextualMapper === identityMapper ? @@ -23137,15 +23138,13 @@ namespace ts { return node; } - function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper: TypeMapper | undefined): Type { + function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper: TypeMapper | undefined, checkMode: CheckMode): Type { const context = getContextNode(node); const saveContextualType = context.contextualType; const saveContextualMapper = context.contextualMapper; context.contextualType = contextualType; context.contextualMapper = contextualMapper; - const checkMode = contextualMapper === identityMapper ? CheckMode.SkipContextSensitive : - contextualMapper ? CheckMode.Inferential : CheckMode.Contextual; - const type = checkExpression(node, checkMode); + const type = checkExpression(node, checkMode | CheckMode.Contextual | (contextualMapper ? CheckMode.Inferential : 0)); // We strip literal freshness when an appropriate contextual type is present such that contextually typed // literals always preserve their literal types (otherwise they might widen during type inference). An alternative // here would be to not mark contextually typed literals as fresh in the first place. @@ -23159,7 +23158,7 @@ namespace ts { function checkExpressionCached(node: Expression, checkMode?: CheckMode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - if (checkMode) { + if (checkMode && checkMode !== CheckMode.Normal) { return checkExpression(node, checkMode); } // When computing a type that we're going to cache, we need to ignore any ongoing control flow @@ -23267,7 +23266,7 @@ namespace ts { } function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration | QualifiedName, type: Type, checkMode?: CheckMode) { - if (checkMode === CheckMode.Inferential) { + if (checkMode && checkMode & CheckMode.Inferential) { const signature = getSingleCallSignature(type); if (signature && signature.typeParameters) { const contextualType = getApparentTypeOfContextualType(node); From 51616a4043bf64a745de1d1e53ca2c6322d58660 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 28 Feb 2019 09:50:57 -0800 Subject: [PATCH 51/83] use sortAndDeduplicate instead of deduplicate --- src/services/refactors/convertToNamedParameters.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 102001a46a8..85073dbc76c 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -60,9 +60,8 @@ namespace ts.refactor.convertToNamedParameters { trailingTriviaOption: textChanges.TrailingTriviaOption.Include }); - - const functionCalls = deduplicate(groupedReferences.functionCalls, equateValues); - forEach(functionCalls, call => { + const functionCalls = sortAndDeduplicate(groupedReferences.functionCalls, /*comparer*/ (a, b) => compareValues(a.pos, b.pos)); + for (const call of functionCalls) { if (call.arguments && call.arguments.length) { const newArgument = getSynthesizedDeepClone(createNewArgument(functionDeclaration, call.arguments), /*includeTrivia*/ true); changes.replaceNodeRange( @@ -71,7 +70,8 @@ namespace ts.refactor.convertToNamedParameters { last(call.arguments), newArgument, { leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, trailingTriviaOption: textChanges.TrailingTriviaOption.Include }); - }}); + } + } } function getGroupedReferences(functionDeclaration: ValidFunctionDeclaration, program: Program, cancellationToken: CancellationToken): GroupedReferences { From 617d5af67e9e6db28c0811667db20390864ac483 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 28 Feb 2019 11:22:05 -0800 Subject: [PATCH 52/83] add diagnostics message for refactor description --- src/compiler/diagnosticMessages.json | 4 ++++ src/services/refactors/convertToNamedParameters.ts | 13 ++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 09b0f721292..3a7c59aacfc 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4871,5 +4871,9 @@ "Enable the 'experimentalDecorators' option in your configuration file": { "category": "Message", "code": 95074 + }, + "Convert to named parameters": { + "category": "Message", + "code": 95075 } } diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 85073dbc76c..bd87aba2ead 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -1,9 +1,6 @@ /* @internal */ namespace ts.refactor.convertToNamedParameters { const refactorName = "Convert to named parameters"; - const refactorDescription = "Convert to named parameters"; - const actionNameNamedParameters = "Convert to named parameters"; - const actionDescriptionNamedParameters = "Convert to named parameters"; const minimumParameterLength = 2; registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); @@ -15,18 +12,20 @@ namespace ts.refactor.convertToNamedParameters { const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker()); if (!functionDeclaration) return emptyArray; + const description = getLocaleSpecificMessage(Diagnostics.Convert_to_named_parameters); return [{ name: refactorName, - description: refactorDescription, + description, + inlineable: false, actions: [{ - name: actionNameNamedParameters, - description: actionDescriptionNamedParameters + name: refactorName, + description }] }]; } function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { - Debug.assert(actionName === actionNameNamedParameters); + Debug.assert(actionName === refactorName); const { file, startPosition, program, cancellationToken, host } = context; const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker()); if (!functionDeclaration || !cancellationToken) return undefined; From 1f91aa1c9b0b20550b5b848898aeddcd91bd8ec7 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 28 Feb 2019 16:55:40 -0800 Subject: [PATCH 53/83] remove inlineable --- src/services/refactors/convertToNamedParameters.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index bd87aba2ead..85d4cda1084 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -16,7 +16,6 @@ namespace ts.refactor.convertToNamedParameters { return [{ name: refactorName, description, - inlineable: false, actions: [{ name: refactorName, description From 23473e021b0006f37d0d2e8f9c5c9de182641cd6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 28 Feb 2019 17:14:00 -0800 Subject: [PATCH 54/83] Skip generic functions along with context sensitive arguments --- src/compiler/checker.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 440dacc7597..e4fbbd6a9a6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -685,6 +685,7 @@ namespace ts { Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable Inferential = 1 << 1, // Inferential typing SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions + SkipGenericFunctions = 1 << 3, // Skip single signature generic functions } const enum CallbackCheck { @@ -20053,7 +20054,8 @@ namespace ts { const paramType = getTypeAtPosition(signature, i); // For context sensitive arguments we pass the identityMapper, which is a signal to treat all // context sensitive function expressions as wildcards - const checkMode = excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0; + const checkMode = (excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | + (excludeArgument ? CheckMode.SkipGenericFunctions : 0); const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); inferTypes(context.inferences, argType, paramType); } @@ -20190,7 +20192,8 @@ namespace ts { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { const paramType = getTypeAtPosition(signature, i); - const checkMode = excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0; + const checkMode = (excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | + (excludeArgument ? CheckMode.SkipGenericFunctions : 0); const argType = checkExpressionWithContextualType(arg, paramType, undefined, checkMode); // If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter), // we obtain the regular type of any object literal arguments because we may not have inferred complete @@ -23266,9 +23269,12 @@ namespace ts { } function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration | QualifiedName, type: Type, checkMode?: CheckMode) { - if (checkMode && checkMode & CheckMode.Inferential) { + if (checkMode && checkMode & (CheckMode.Inferential | CheckMode.SkipGenericFunctions)) { const signature = getSingleCallSignature(type); if (signature && signature.typeParameters) { + if (checkMode & CheckMode.SkipGenericFunctions) { + return anyFunctionType; + } const contextualType = getApparentTypeOfContextualType(node); if (contextualType) { const contextualSignature = getSingleCallSignature(getNonNullableType(contextualType)); @@ -23278,7 +23284,6 @@ namespace ts { } } } - return type; } From 35ebbece49320b48df5a0cb048a08a34256718e0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 1 Mar 2019 09:12:24 -0800 Subject: [PATCH 55/83] Minor fixes --- src/compiler/checker.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0d9363dc1d8..bce50c821a5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20036,7 +20036,12 @@ namespace ts { // Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec) function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer): Signature { const context = createInferenceContext(signature.typeParameters!, signature, InferenceFlags.None, compareTypes); - const sourceSignature = contextualMapper ? instantiateSignature(contextualSignature, contextualMapper) : contextualSignature; + // We clone the contextualMapper to avoid fixing. For example, when the source signature is (x: T) => T[] and + // the contextual signature is (...args: A) => B, we want to infer the element type of A's constraint (say 'any') + // for T but leave it possible to later infer '[any]' back to A. + const restType = getEffectiveRestType(contextualSignature); + const mapper = contextualMapper && restType && restType.flags & TypeFlags.TypeParameter ? cloneTypeMapper(contextualMapper) : contextualMapper; + const sourceSignature = mapper ? instantiateSignature(contextualSignature, mapper) : contextualSignature; forEachMatchingParameterType(sourceSignature, signature, (source, target) => { // Type parameters from outer context referenced by source type are fixed by instantiation of the source type inferTypes(context.inferences, source, target); @@ -20221,7 +20226,7 @@ namespace ts { // can be specified by users through attributes property. const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); const checkMode = excludeArgument && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; - const attributesType = checkExpressionWithContextualType(node.attributes, paramType, undefined, checkMode); + const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined, checkMode); return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes); } @@ -20257,7 +20262,7 @@ namespace ts { const paramType = getTypeAtPosition(signature, i); const checkMode = (excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | (excludeArgument ? CheckMode.SkipGenericFunctions : 0); - const argType = checkExpressionWithContextualType(arg, paramType, undefined, checkMode); + const argType = checkExpressionWithContextualType(arg, paramType, /*contextualMapper*/ undefined, checkMode); // If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter), // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). @@ -21874,7 +21879,7 @@ namespace ts { const functionFlags = getFunctionFlags(func); let type: Type; if (func.body.kind !== SyntaxKind.Block) { - type = checkExpressionCached(func.body, checkMode); + type = checkExpressionCached(func.body, checkMode && checkMode & ~CheckMode.SkipGenericFunctions); if (functionFlags & FunctionFlags.Async) { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the @@ -22064,7 +22069,7 @@ namespace ts { forEachReturnStatement(func.body, returnStatement => { const expr = returnStatement.expression; if (expr) { - let type = checkExpressionCached(expr, checkMode); + let type = checkExpressionCached(expr, checkMode && checkMode && checkMode & ~CheckMode.SkipGenericFunctions); if (functionFlags & FunctionFlags.Async) { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the From cd060e75feff11cd47d2c3fe44dddf9d2e837ab7 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 1 Mar 2019 09:17:44 -0800 Subject: [PATCH 56/83] use interface for ValidVariableDeclaration --- src/services/refactors/convertToNamedParameters.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 85d4cda1084..041000c091a 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -469,7 +469,10 @@ namespace ts.refactor.convertToNamedParameters { type ValidParameterNodeArray = NodeArray; - type ValidVariableDeclaration = VariableDeclaration & { name: Identifier, type: undefined }; + interface ValidVariableDeclaration extends VariableDeclaration { + name: Identifier; + type: undefined; + } interface ValidConstructor extends ConstructorDeclaration { parent: (ClassDeclaration & { name: Identifier }) | (ClassExpression & { parent: ValidVariableDeclaration }); From 98a146dcfcd749b437f086a3802285e93b526127 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 1 Mar 2019 13:23:59 -0800 Subject: [PATCH 57/83] use getTouchingToken --- src/services/refactors/convertToNamedParameters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index 041000c091a..b4220287bd0 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -233,7 +233,7 @@ namespace ts.refactor.convertToNamedParameters { } function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined { - const node = getTokenAtPosition(file, startPosition); + const node = getTouchingToken(file, startPosition); const functionDeclaration = getContainingFunction(node); if (functionDeclaration && isValidFunctionDeclaration(functionDeclaration, checker) From 91f8fc60f1b0cc42c7842d71ee5fe7a0b2f37595 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 2 Mar 2019 10:34:23 -0800 Subject: [PATCH 58/83] Defer calls to generic functions returning generic functions --- src/compiler/checker.ts | 86 +++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bce50c821a5..a8dccb072b1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -228,9 +228,9 @@ namespace ts { isContextSensitive, getFullyQualifiedName, getResolvedSignature: (node, candidatesOutArray, agumentCount) => - getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, /*isForSignatureHelp*/ false), + getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, CheckMode.Normal), getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, agumentCount) => - getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, /*isForSignatureHelp*/ true), + getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, CheckMode.IsForSignatureHelp), getConstantValue: nodeIn => { const node = getParseTreeNode(nodeIn, canHaveConstantValue); return node ? getConstantValue(node) : undefined; @@ -374,10 +374,10 @@ namespace ts { getLocalTypeParametersOfClassOrInterfaceOrTypeAlias, }; - function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, isForSignatureHelp: boolean): Signature | undefined { + function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined { const node = getParseTreeNode(nodeIn, isCallLikeExpression); apparentArgumentCount = argumentCount; - const res = node ? getResolvedSignature(node, candidatesOutArray, isForSignatureHelp) : undefined; + const res = node ? getResolvedSignature(node, candidatesOutArray, checkMode) : undefined; apparentArgumentCount = undefined; return res; } @@ -693,6 +693,7 @@ namespace ts { Inferential = 1 << 1, // Inferential typing SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions SkipGenericFunctions = 1 << 3, // Skip single signature generic functions + IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help } const enum CallbackCheck { @@ -20482,7 +20483,7 @@ namespace ts { return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount); } - function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean, fallbackError?: DiagnosticMessage): Signature { + function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, fallbackError?: DiagnosticMessage): Signature { const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression; const isDecorator = node.kind === SyntaxKind.Decorator; const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node); @@ -20555,7 +20556,7 @@ namespace ts { // If we are in signature help, a trailing comma indicates that we intend to provide another argument, // so we will only accept overloads with arity at least 1 higher than the current number of provided arguments. const signatureHelpTrailingComma = - isForSignatureHelp && node.kind === SyntaxKind.CallExpression && node.arguments.hasTrailingComma; + !!(checkMode & CheckMode.IsForSignatureHelp) && node.kind === SyntaxKind.CallExpression && node.arguments.hasTrailingComma; // Section 4.12.1: // if the candidate list contains one or more signatures for which the type of each argument @@ -20837,7 +20838,7 @@ namespace ts { return maxParamsIndex; } - function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature { + function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { if (node.expression.kind === SyntaxKind.SuperKeyword) { const superType = checkSuperExpression(node.expression); if (isTypeAny(superType)) { @@ -20852,7 +20853,7 @@ namespace ts { const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!); if (baseTypeNode) { const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode); - return resolveCall(node, baseConstructors, candidatesOutArray, isForSignatureHelp); + return resolveCall(node, baseConstructors, candidatesOutArray, checkMode); } } return resolveUntypedCall(node); @@ -20912,12 +20913,22 @@ namespace ts { } return resolveErrorCall(node); } + // If we are skipping generic functions (i.e. this call is an argument to another call for which context + // sensitive arguments are being deferred) and every call signature is generic and returns a function type, + // we return resolvingSignature here. This result will be propagated out and turned into anyFunctionType. + if (checkMode & CheckMode.SkipGenericFunctions && callSignatures.every(isGenericFunctionReturningFunction)) { + return resolvingSignature; + } // If the function is explicitly marked with `@class`, then it must be constructed. if (callSignatures.some(sig => isInJSFile(sig.declaration) && !!getJSDocClassTag(sig.declaration!))) { error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType)); return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp); + return resolveCall(node, callSignatures, candidatesOutArray, checkMode); + } + + function isGenericFunctionReturningFunction(signature: Signature) { + return !!(signature.typeParameters && getSingleCallSignature(getReturnTypeOfSignature(signature))); } /** @@ -20931,7 +20942,7 @@ namespace ts { !numCallSignatures && !numConstructSignatures && !(apparentFuncType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType); } - function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature { + function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { if (node.arguments && languageVersion < ScriptTarget.ES5) { const spreadIndex = getSpreadArgumentIndex(node.arguments); if (spreadIndex >= 0) { @@ -20984,7 +20995,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, constructSignatures, candidatesOutArray, isForSignatureHelp); + return resolveCall(node, constructSignatures, candidatesOutArray, checkMode); } // If expressionType's apparent type is an object type with no construct signatures but @@ -20993,7 +21004,7 @@ namespace ts { // operation is Any. It is an error to have a Void this type. const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call); if (callSignatures.length) { - const signature = resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp); + const signature = resolveCall(node, callSignatures, candidatesOutArray, checkMode); if (!noImplicitAny) { if (signature.declaration && !isJSConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) { error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); @@ -21103,7 +21114,7 @@ namespace ts { } } - function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature { + function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { const tagType = checkExpression(node.tag); const apparentType = getApparentType(tagType); @@ -21124,7 +21135,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp); + return resolveCall(node, callSignatures, candidatesOutArray, checkMode); } /** @@ -21155,7 +21166,7 @@ namespace ts { /** * Resolves a decorator as if it were a call expression. */ - function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature { + function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { const funcType = checkExpression(node.expression); const apparentType = getApparentType(funcType); if (apparentType === errorType) { @@ -21184,7 +21195,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp, headMessage); + return resolveCall(node, callSignatures, candidatesOutArray, checkMode, headMessage); } function createSignatureForJSXIntrinsic(node: JsxOpeningLikeElement, result: Type): Signature { @@ -21213,7 +21224,7 @@ namespace ts { ); } - function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature { + function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { if (isJsxIntrinsicIdentifier(node.tagName)) { const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); const fakeSignature = createSignatureForJSXIntrinsic(node, result); @@ -21237,7 +21248,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, signatures, candidatesOutArray, isForSignatureHelp); + return resolveCall(node, signatures, candidatesOutArray, checkMode); } /** @@ -21252,19 +21263,19 @@ namespace ts { signature.parameters.length < getDecoratorArgumentCount(decorator, signature)); } - function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature { + function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { switch (node.kind) { case SyntaxKind.CallExpression: - return resolveCallExpression(node, candidatesOutArray, isForSignatureHelp); + return resolveCallExpression(node, candidatesOutArray, checkMode); case SyntaxKind.NewExpression: - return resolveNewExpression(node, candidatesOutArray, isForSignatureHelp); + return resolveNewExpression(node, candidatesOutArray, checkMode); case SyntaxKind.TaggedTemplateExpression: - return resolveTaggedTemplateExpression(node, candidatesOutArray, isForSignatureHelp); + return resolveTaggedTemplateExpression(node, candidatesOutArray, checkMode); case SyntaxKind.Decorator: - return resolveDecorator(node, candidatesOutArray, isForSignatureHelp); + return resolveDecorator(node, candidatesOutArray, checkMode); case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxSelfClosingElement: - return resolveJsxOpeningLikeElement(node, candidatesOutArray, isForSignatureHelp); + return resolveJsxOpeningLikeElement(node, candidatesOutArray, checkMode); } throw Debug.assertNever(node, "Branch in 'resolveSignature' should be unreachable."); } @@ -21276,7 +21287,7 @@ namespace ts { * the function will fill it up with appropriate candidate signatures * @return a signature of the call-like expression or undefined if one can't be found */ - function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[] | undefined, isForSignatureHelp = false): Signature { + function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[] | undefined, checkMode?: CheckMode): Signature { const links = getNodeLinks(node); // If getResolvedSignature has already been called, we will have cached the resolvedSignature. // However, it is possible that either candidatesOutArray was not passed in the first time, @@ -21287,11 +21298,15 @@ namespace ts { return cached; } links.resolvedSignature = resolvingSignature; - const result = resolveSignature(node, candidatesOutArray, isForSignatureHelp); - // If signature resolution originated in control flow type analysis (for example to compute the - // assigned type in a flow assignment) we don't cache the result as it may be based on temporary - // types from the control flow analysis. - links.resolvedSignature = flowLoopStart === flowLoopCount ? result : cached; + const result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal); + // When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call + // resolution should be deferred. + if (result !== resolvingSignature) { + // If signature resolution originated in control flow type analysis (for example to compute the + // assigned type in a flow assignment) we don't cache the result as it may be based on temporary + // types from the control flow analysis. + links.resolvedSignature = flowLoopStart === flowLoopCount ? result : cached; + } return result; } @@ -21375,10 +21390,15 @@ namespace ts { * @param node The call/new expression to be checked. * @returns On success, the expression's signature's return type. On failure, anyType. */ - function checkCallExpression(node: CallExpression | NewExpression): Type { + function checkCallExpression(node: CallExpression | NewExpression, checkMode?: CheckMode): Type { if (!checkGrammarTypeArguments(node, node.typeArguments)) checkGrammarArguments(node.arguments); - const signature = getResolvedSignature(node); + const signature = getResolvedSignature(node, /*candidatesOutArray*/ undefined, checkMode); + if (signature === resolvingSignature) { + // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that + // returns a function type. We defer checking and return anyFunctionType. + return anyFunctionType; + } if (node.expression.kind === SyntaxKind.SuperKeyword) { return voidType; @@ -23505,7 +23525,7 @@ namespace ts { } /* falls through */ case SyntaxKind.NewExpression: - return checkCallExpression(node); + return checkCallExpression(node, checkMode); case SyntaxKind.TaggedTemplateExpression: return checkTaggedTemplateExpression(node); case SyntaxKind.ParenthesizedExpression: From 304e25cf69a9570234d8c2ad83c1a1f55b086635 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 2 Mar 2019 11:59:33 -0800 Subject: [PATCH 59/83] Add tests --- .../compiler/genericFunctionInference1.ts | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/cases/compiler/genericFunctionInference1.ts diff --git a/tests/cases/compiler/genericFunctionInference1.ts b/tests/cases/compiler/genericFunctionInference1.ts new file mode 100644 index 00000000000..c5416f32bda --- /dev/null +++ b/tests/cases/compiler/genericFunctionInference1.ts @@ -0,0 +1,82 @@ +// @strict: true +// @target: es2015 + +declare function pipe(ab: (...args: A) => B): (...args: A) => B; +declare function pipe(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; +declare function pipe(ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; + +declare function list(a: T): T[]; +declare function box(x: V): { value: V }; + +const f00 = pipe(list); +const f01 = pipe(list, box); +const f02 = pipe(x => list(x), box); +const f03 = pipe(list, x => box(x)); +const f04 = pipe(x => list(x), x => box(x)) +const f05 = pipe(list, pipe(box)); +const f06 = pipe(x => list(x), pipe(box)); +const f07 = pipe(x => list(x), pipe(x => box(x))); + +const f10: (x: T) => T[] = pipe(list); +const f11: (x: T) => { value: T[] } = pipe(list, box); +const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); +const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); +const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) +const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); +const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); +const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); + +// #29904.2 + +const fn20 = pipe((_a?: {}) => 1); + +// #29904.3 + +type Fn = (n: number) => number; +const fn30: Fn = pipe( + x => x + 1, + x => x * 2, +); + +const promise = Promise.resolve(1); +promise.then( + pipe( + x => x + 1, + x => x * 2, + ), +); + +// #29904.4 + +declare const getString: () => string; +declare const orUndefined: (name: string) => string | undefined; +declare const identity: (value: T) => T; + +const fn40 = pipe( + getString, + string => orUndefined(string), + identity, +); + +// #29904.6 + +declare const getArray: () => string[]; +declare const first: (ts: T[]) => T; + +const fn60 = pipe( + getArray, + x => x, + first, +); + +const fn61 = pipe( + getArray, + identity, + first, +); + +const fn62 = pipe( + getArray, + x => x, + x => first(x), +); From e7881a412ed1ff64f18ecd34486e253e91bece11 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 2 Mar 2019 11:59:41 -0800 Subject: [PATCH 60/83] Accept new baselines --- .../reference/genericFunctionInference1.js | 109 +++++ .../genericFunctionInference1.symbols | 381 +++++++++++++++++ .../reference/genericFunctionInference1.types | 397 ++++++++++++++++++ 3 files changed, 887 insertions(+) create mode 100644 tests/baselines/reference/genericFunctionInference1.js create mode 100644 tests/baselines/reference/genericFunctionInference1.symbols create mode 100644 tests/baselines/reference/genericFunctionInference1.types diff --git a/tests/baselines/reference/genericFunctionInference1.js b/tests/baselines/reference/genericFunctionInference1.js new file mode 100644 index 00000000000..872656ba9c5 --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference1.js @@ -0,0 +1,109 @@ +//// [genericFunctionInference1.ts] +declare function pipe(ab: (...args: A) => B): (...args: A) => B; +declare function pipe(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; +declare function pipe(ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; + +declare function list(a: T): T[]; +declare function box(x: V): { value: V }; + +const f00 = pipe(list); +const f01 = pipe(list, box); +const f02 = pipe(x => list(x), box); +const f03 = pipe(list, x => box(x)); +const f04 = pipe(x => list(x), x => box(x)) +const f05 = pipe(list, pipe(box)); +const f06 = pipe(x => list(x), pipe(box)); +const f07 = pipe(x => list(x), pipe(x => box(x))); + +const f10: (x: T) => T[] = pipe(list); +const f11: (x: T) => { value: T[] } = pipe(list, box); +const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); +const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); +const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) +const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); +const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); +const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); + +// #29904.2 + +const fn20 = pipe((_a?: {}) => 1); + +// #29904.3 + +type Fn = (n: number) => number; +const fn30: Fn = pipe( + x => x + 1, + x => x * 2, +); + +const promise = Promise.resolve(1); +promise.then( + pipe( + x => x + 1, + x => x * 2, + ), +); + +// #29904.4 + +declare const getString: () => string; +declare const orUndefined: (name: string) => string | undefined; +declare const identity: (value: T) => T; + +const fn40 = pipe( + getString, + string => orUndefined(string), + identity, +); + +// #29904.6 + +declare const getArray: () => string[]; +declare const first: (ts: T[]) => T; + +const fn60 = pipe( + getArray, + x => x, + first, +); + +const fn61 = pipe( + getArray, + identity, + first, +); + +const fn62 = pipe( + getArray, + x => x, + x => first(x), +); + + +//// [genericFunctionInference1.js] +"use strict"; +const f00 = pipe(list); +const f01 = pipe(list, box); +const f02 = pipe(x => list(x), box); +const f03 = pipe(list, x => box(x)); +const f04 = pipe(x => list(x), x => box(x)); +const f05 = pipe(list, pipe(box)); +const f06 = pipe(x => list(x), pipe(box)); +const f07 = pipe(x => list(x), pipe(x => box(x))); +const f10 = pipe(list); +const f11 = pipe(list, box); +const f12 = pipe(x => list(x), box); +const f13 = pipe(list, x => box(x)); +const f14 = pipe(x => list(x), x => box(x)); +const f15 = pipe(list, pipe(box)); +const f16 = pipe(x => list(x), pipe(box)); +const f17 = pipe(x => list(x), pipe(x => box(x))); +// #29904.2 +const fn20 = pipe((_a) => 1); +const fn30 = pipe(x => x + 1, x => x * 2); +const promise = Promise.resolve(1); +promise.then(pipe(x => x + 1, x => x * 2)); +const fn40 = pipe(getString, string => orUndefined(string), identity); +const fn60 = pipe(getArray, x => x, first); +const fn61 = pipe(getArray, identity, first); +const fn62 = pipe(getArray, x => x, x => first(x)); diff --git a/tests/baselines/reference/genericFunctionInference1.symbols b/tests/baselines/reference/genericFunctionInference1.symbols new file mode 100644 index 00000000000..d49a20cec9c --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference1.symbols @@ -0,0 +1,381 @@ +=== tests/cases/compiler/genericFunctionInference1.ts === +declare function pipe(ab: (...args: A) => B): (...args: A) => B; +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 0, 22)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 0, 38)) +>ab : Symbol(ab, Decl(genericFunctionInference1.ts, 0, 42)) +>args : Symbol(args, Decl(genericFunctionInference1.ts, 0, 47)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 0, 22)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 0, 38)) +>args : Symbol(args, Decl(genericFunctionInference1.ts, 0, 67)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 0, 22)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 0, 38)) + +declare function pipe(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 1, 22)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 1, 38)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 1, 41)) +>ab : Symbol(ab, Decl(genericFunctionInference1.ts, 1, 45)) +>args : Symbol(args, Decl(genericFunctionInference1.ts, 1, 50)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 1, 22)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 1, 38)) +>bc : Symbol(bc, Decl(genericFunctionInference1.ts, 1, 67)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 1, 73)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 1, 38)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 1, 41)) +>args : Symbol(args, Decl(genericFunctionInference1.ts, 1, 87)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 1, 22)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 1, 41)) + +declare function pipe(ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 2, 22)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 2, 38)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 2, 41)) +>D : Symbol(D, Decl(genericFunctionInference1.ts, 2, 44)) +>ab : Symbol(ab, Decl(genericFunctionInference1.ts, 2, 48)) +>args : Symbol(args, Decl(genericFunctionInference1.ts, 2, 53)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 2, 22)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 2, 38)) +>bc : Symbol(bc, Decl(genericFunctionInference1.ts, 2, 70)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 2, 76)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 2, 38)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 2, 41)) +>cd : Symbol(cd, Decl(genericFunctionInference1.ts, 2, 87)) +>c : Symbol(c, Decl(genericFunctionInference1.ts, 2, 93)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 2, 41)) +>D : Symbol(D, Decl(genericFunctionInference1.ts, 2, 44)) +>args : Symbol(args, Decl(genericFunctionInference1.ts, 2, 107)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 2, 22)) +>D : Symbol(D, Decl(genericFunctionInference1.ts, 2, 44)) + +declare function list(a: T): T[]; +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 4, 22)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 4, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 4, 22)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 4, 22)) + +declare function box(x: V): { value: V }; +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>V : Symbol(V, Decl(genericFunctionInference1.ts, 5, 21)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 5, 24)) +>V : Symbol(V, Decl(genericFunctionInference1.ts, 5, 21)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 5, 32)) +>V : Symbol(V, Decl(genericFunctionInference1.ts, 5, 21)) + +const f00 = pipe(list); +>f00 : Symbol(f00, Decl(genericFunctionInference1.ts, 7, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +const f01 = pipe(list, box); +>f01 : Symbol(f01, Decl(genericFunctionInference1.ts, 8, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f02 = pipe(x => list(x), box); +>f02 : Symbol(f02, Decl(genericFunctionInference1.ts, 9, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 9, 17)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 9, 17)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f03 = pipe(list, x => box(x)); +>f03 : Symbol(f03, Decl(genericFunctionInference1.ts, 10, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 10, 22)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 10, 22)) + +const f04 = pipe(x => list(x), x => box(x)) +>f04 : Symbol(f04, Decl(genericFunctionInference1.ts, 11, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 11, 17)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 11, 17)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 11, 30)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 11, 30)) + +const f05 = pipe(list, pipe(box)); +>f05 : Symbol(f05, Decl(genericFunctionInference1.ts, 12, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f06 = pipe(x => list(x), pipe(box)); +>f06 : Symbol(f06, Decl(genericFunctionInference1.ts, 13, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 13, 17)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 13, 17)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f07 = pipe(x => list(x), pipe(x => box(x))); +>f07 : Symbol(f07, Decl(genericFunctionInference1.ts, 14, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 14, 17)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 14, 17)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 14, 36)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 14, 36)) + +const f10: (x: T) => T[] = pipe(list); +>f10 : Symbol(f10, Decl(genericFunctionInference1.ts, 16, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 16, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 16, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 16, 12)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 16, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +const f11: (x: T) => { value: T[] } = pipe(list, box); +>f11 : Symbol(f11, Decl(genericFunctionInference1.ts, 17, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 17, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 17, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 17, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 17, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 17, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); +>f12 : Symbol(f12, Decl(genericFunctionInference1.ts, 18, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 18, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 18, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 18, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 18, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 18, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 18, 46)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 18, 46)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); +>f13 : Symbol(f13, Decl(genericFunctionInference1.ts, 19, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 19, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 19, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 19, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 19, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 19, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 19, 51)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 19, 51)) + +const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) +>f14 : Symbol(f14, Decl(genericFunctionInference1.ts, 20, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 20, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 20, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 20, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 20, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 46)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 46)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 59)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 59)) + +const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); +>f15 : Symbol(f15, Decl(genericFunctionInference1.ts, 21, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 21, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 21, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 21, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 21, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 21, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); +>f16 : Symbol(f16, Decl(genericFunctionInference1.ts, 22, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 22, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 22, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 22, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 22, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 22, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 22, 46)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 22, 46)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); +>f17 : Symbol(f17, Decl(genericFunctionInference1.ts, 23, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 23, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 23, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 23, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 23, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 46)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 46)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 65)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 65)) + +// #29904.2 + +const fn20 = pipe((_a?: {}) => 1); +>fn20 : Symbol(fn20, Decl(genericFunctionInference1.ts, 27, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>_a : Symbol(_a, Decl(genericFunctionInference1.ts, 27, 19)) + +// #29904.3 + +type Fn = (n: number) => number; +>Fn : Symbol(Fn, Decl(genericFunctionInference1.ts, 27, 34)) +>n : Symbol(n, Decl(genericFunctionInference1.ts, 31, 11)) + +const fn30: Fn = pipe( +>fn30 : Symbol(fn30, Decl(genericFunctionInference1.ts, 32, 5)) +>Fn : Symbol(Fn, Decl(genericFunctionInference1.ts, 27, 34)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) + + x => x + 1, +>x : Symbol(x, Decl(genericFunctionInference1.ts, 32, 22)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 32, 22)) + + x => x * 2, +>x : Symbol(x, Decl(genericFunctionInference1.ts, 33, 15)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 33, 15)) + +); + +const promise = Promise.resolve(1); +>promise : Symbol(promise, Decl(genericFunctionInference1.ts, 37, 5)) +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) + +promise.then( +>promise.then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>promise : Symbol(promise, Decl(genericFunctionInference1.ts, 37, 5)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) + + pipe( +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) + + x => x + 1, +>x : Symbol(x, Decl(genericFunctionInference1.ts, 39, 9)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 39, 9)) + + x => x * 2, +>x : Symbol(x, Decl(genericFunctionInference1.ts, 40, 19)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 40, 19)) + + ), +); + +// #29904.4 + +declare const getString: () => string; +>getString : Symbol(getString, Decl(genericFunctionInference1.ts, 47, 13)) + +declare const orUndefined: (name: string) => string | undefined; +>orUndefined : Symbol(orUndefined, Decl(genericFunctionInference1.ts, 48, 13)) +>name : Symbol(name, Decl(genericFunctionInference1.ts, 48, 28)) + +declare const identity: (value: T) => T; +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 49, 13)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 49, 25)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 49, 28)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 49, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 49, 25)) + +const fn40 = pipe( +>fn40 : Symbol(fn40, Decl(genericFunctionInference1.ts, 51, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) + + getString, +>getString : Symbol(getString, Decl(genericFunctionInference1.ts, 47, 13)) + + string => orUndefined(string), +>string : Symbol(string, Decl(genericFunctionInference1.ts, 52, 14)) +>orUndefined : Symbol(orUndefined, Decl(genericFunctionInference1.ts, 48, 13)) +>string : Symbol(string, Decl(genericFunctionInference1.ts, 52, 14)) + + identity, +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 49, 13)) + +); + +// #29904.6 + +declare const getArray: () => string[]; +>getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 59, 13)) + +declare const first: (ts: T[]) => T; +>first : Symbol(first, Decl(genericFunctionInference1.ts, 60, 13)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 60, 22)) +>ts : Symbol(ts, Decl(genericFunctionInference1.ts, 60, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 60, 22)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 60, 22)) + +const fn60 = pipe( +>fn60 : Symbol(fn60, Decl(genericFunctionInference1.ts, 62, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) + + getArray, +>getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 59, 13)) + + x => x, +>x : Symbol(x, Decl(genericFunctionInference1.ts, 63, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 63, 13)) + + first, +>first : Symbol(first, Decl(genericFunctionInference1.ts, 60, 13)) + +); + +const fn61 = pipe( +>fn61 : Symbol(fn61, Decl(genericFunctionInference1.ts, 68, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) + + getArray, +>getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 59, 13)) + + identity, +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 49, 13)) + + first, +>first : Symbol(first, Decl(genericFunctionInference1.ts, 60, 13)) + +); + +const fn62 = pipe( +>fn62 : Symbol(fn62, Decl(genericFunctionInference1.ts, 74, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) + + getArray, +>getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 59, 13)) + + x => x, +>x : Symbol(x, Decl(genericFunctionInference1.ts, 75, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 75, 13)) + + x => first(x), +>x : Symbol(x, Decl(genericFunctionInference1.ts, 76, 11)) +>first : Symbol(first, Decl(genericFunctionInference1.ts, 60, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 76, 11)) + +); + diff --git a/tests/baselines/reference/genericFunctionInference1.types b/tests/baselines/reference/genericFunctionInference1.types new file mode 100644 index 00000000000..037e4a49718 --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference1.types @@ -0,0 +1,397 @@ +=== tests/cases/compiler/genericFunctionInference1.ts === +declare function pipe(ab: (...args: A) => B): (...args: A) => B; +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>ab : (...args: A) => B +>args : A +>args : A + +declare function pipe(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>ab : (...args: A) => B +>args : A +>bc : (b: B) => C +>b : B +>args : A + +declare function pipe(ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>ab : (...args: A) => B +>args : A +>bc : (b: B) => C +>b : B +>cd : (c: C) => D +>c : C +>args : A + +declare function list(a: T): T[]; +>list : (a: T) => T[] +>a : T + +declare function box(x: V): { value: V }; +>box : (x: V) => { value: V; } +>x : V +>value : V + +const f00 = pipe(list); +>f00 : (a: any) => any[] +>pipe(list) : (a: any) => any[] +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] + +const f01 = pipe(list, box); +>f01 : (a: any) => { value: any[]; } +>pipe(list, box) : (a: any) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] +>box : (x: V) => { value: V; } + +const f02 = pipe(x => list(x), box); +>f02 : (x: any) => { value: any[]; } +>pipe(x => list(x), box) : (x: any) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => list(x) : (x: any) => any[] +>x : any +>list(x) : any[] +>list : (a: T) => T[] +>x : any +>box : (x: V) => { value: V; } + +const f03 = pipe(list, x => box(x)); +>f03 : (a: any) => { value: any[]; } +>pipe(list, x => box(x)) : (a: any) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] +>x => box(x) : (x: any[]) => { value: any[]; } +>x : any[] +>box(x) : { value: any[]; } +>box : (x: V) => { value: V; } +>x : any[] + +const f04 = pipe(x => list(x), x => box(x)) +>f04 : (x: any) => { value: any[]; } +>pipe(x => list(x), x => box(x)) : (x: any) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => list(x) : (x: any) => any[] +>x : any +>list(x) : any[] +>list : (a: T) => T[] +>x : any +>x => box(x) : (x: any[]) => { value: any[]; } +>x : any[] +>box(x) : { value: any[]; } +>box : (x: V) => { value: V; } +>x : any[] + +const f05 = pipe(list, pipe(box)); +>f05 : (a: any) => { value: any[]; } +>pipe(list, pipe(box)) : (a: any) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] +>pipe(box) : (x: any[]) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>box : (x: V) => { value: V; } + +const f06 = pipe(x => list(x), pipe(box)); +>f06 : (x: any) => { value: any[]; } +>pipe(x => list(x), pipe(box)) : (x: any) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => list(x) : (x: any) => any[] +>x : any +>list(x) : any[] +>list : (a: T) => T[] +>x : any +>pipe(box) : (x: any[]) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>box : (x: V) => { value: V; } + +const f07 = pipe(x => list(x), pipe(x => box(x))); +>f07 : (x: any) => { value: any[]; } +>pipe(x => list(x), pipe(x => box(x))) : (x: any) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => list(x) : (x: any) => any[] +>x : any +>list(x) : any[] +>list : (a: T) => T[] +>x : any +>pipe(x => box(x)) : (x: any[]) => { value: any[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => box(x) : (x: any[]) => { value: any[]; } +>x : any[] +>box(x) : { value: any[]; } +>box : (x: V) => { value: V; } +>x : any[] + +const f10: (x: T) => T[] = pipe(list); +>f10 : (x: T) => T[] +>x : T +>pipe(list) : (a: T) => T[] +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] + +const f11: (x: T) => { value: T[] } = pipe(list, box); +>f11 : (x: T) => { value: T[]; } +>x : T +>value : T[] +>pipe(list, box) : (a: T) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] +>box : (x: V) => { value: V; } + +const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); +>f12 : (x: T) => { value: T[]; } +>x : T +>value : T[] +>pipe(x => list(x), box) : (x: T) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => list(x) : (x: T) => T[] +>x : T +>list(x) : T[] +>list : (a: T) => T[] +>x : T +>box : (x: V) => { value: V; } + +const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); +>f13 : (x: T) => { value: T[]; } +>x : T +>value : T[] +>pipe(list, x => box(x)) : (a: T) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] +>x => box(x) : (x: T[]) => { value: T[]; } +>x : T[] +>box(x) : { value: T[]; } +>box : (x: V) => { value: V; } +>x : T[] + +const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) +>f14 : (x: T) => { value: T[]; } +>x : T +>value : T[] +>pipe(x => list(x), x => box(x)) : (x: T) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => list(x) : (x: T) => T[] +>x : T +>list(x) : T[] +>list : (a: T) => T[] +>x : T +>x => box(x) : (x: T[]) => { value: T[]; } +>x : T[] +>box(x) : { value: T[]; } +>box : (x: V) => { value: V; } +>x : T[] + +const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); +>f15 : (x: T) => { value: T[]; } +>x : T +>value : T[] +>pipe(list, pipe(box)) : (a: T) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] +>pipe(box) : (x: T[]) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>box : (x: V) => { value: V; } + +const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); +>f16 : (x: T) => { value: T[]; } +>x : T +>value : T[] +>pipe(x => list(x), pipe(box)) : (x: T) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => list(x) : (x: T) => T[] +>x : T +>list(x) : T[] +>list : (a: T) => T[] +>x : T +>pipe(box) : (x: T[]) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>box : (x: V) => { value: V; } + +const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); +>f17 : (x: T) => { value: T[]; } +>x : T +>value : T[] +>pipe(x => list(x), pipe(x => box(x))) : (x: T) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => list(x) : (x: T) => T[] +>x : T +>list(x) : T[] +>list : (a: T) => T[] +>x : T +>pipe(x => box(x)) : (x: T[]) => { value: T[]; } +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>x => box(x) : (x: T[]) => { value: T[]; } +>x : T[] +>box(x) : { value: T[]; } +>box : (x: V) => { value: V; } +>x : T[] + +// #29904.2 + +const fn20 = pipe((_a?: {}) => 1); +>fn20 : (_a?: {} | undefined) => number +>pipe((_a?: {}) => 1) : (_a?: {} | undefined) => number +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>(_a?: {}) => 1 : (_a?: {} | undefined) => number +>_a : {} | undefined +>1 : 1 + +// #29904.3 + +type Fn = (n: number) => number; +>Fn : Fn +>n : number + +const fn30: Fn = pipe( +>fn30 : Fn +>pipe( x => x + 1, x => x * 2,) : (x: number) => number +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } + + x => x + 1, +>x => x + 1 : (x: number) => number +>x : number +>x + 1 : number +>x : number +>1 : 1 + + x => x * 2, +>x => x * 2 : (x: number) => number +>x : number +>x * 2 : number +>x : number +>2 : 2 + +); + +const promise = Promise.resolve(1); +>promise : Promise +>Promise.resolve(1) : Promise +>Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>Promise : PromiseConstructor +>resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>1 : 1 + +promise.then( +>promise.then( pipe( x => x + 1, x => x * 2, ),) : Promise +>promise.then : (onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +>promise : Promise +>then : (onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise + + pipe( +>pipe( x => x + 1, x => x * 2, ) : (x: number) => number +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } + + x => x + 1, +>x => x + 1 : (x: number) => number +>x : number +>x + 1 : number +>x : number +>1 : 1 + + x => x * 2, +>x => x * 2 : (x: number) => number +>x : number +>x * 2 : number +>x : number +>2 : 2 + + ), +); + +// #29904.4 + +declare const getString: () => string; +>getString : () => string + +declare const orUndefined: (name: string) => string | undefined; +>orUndefined : (name: string) => string | undefined +>name : string + +declare const identity: (value: T) => T; +>identity : (value: T) => T +>value : T + +const fn40 = pipe( +>fn40 : () => string | undefined +>pipe( getString, string => orUndefined(string), identity,) : () => string | undefined +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } + + getString, +>getString : () => string + + string => orUndefined(string), +>string => orUndefined(string) : (string: string) => string | undefined +>string : string +>orUndefined(string) : string | undefined +>orUndefined : (name: string) => string | undefined +>string : string + + identity, +>identity : (value: T) => T + +); + +// #29904.6 + +declare const getArray: () => string[]; +>getArray : () => string[] + +declare const first: (ts: T[]) => T; +>first : (ts: T[]) => T +>ts : T[] + +const fn60 = pipe( +>fn60 : () => string +>pipe( getArray, x => x, first,) : () => string +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } + + getArray, +>getArray : () => string[] + + x => x, +>x => x : (x: string[]) => string[] +>x : string[] +>x : string[] + + first, +>first : (ts: T[]) => T + +); + +const fn61 = pipe( +>fn61 : () => string +>pipe( getArray, identity, first,) : () => string +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } + + getArray, +>getArray : () => string[] + + identity, +>identity : (value: T) => T + + first, +>first : (ts: T[]) => T + +); + +const fn62 = pipe( +>fn62 : () => string +>pipe( getArray, x => x, x => first(x),) : () => string +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } + + getArray, +>getArray : () => string[] + + x => x, +>x => x : (x: string[]) => string[] +>x : string[] +>x : string[] + + x => first(x), +>x => first(x) : (x: string[]) => string +>x : string[] +>first(x) : string +>first : (ts: T[]) => T +>x : string[] + +); + From c344ef3123023d71812290e96109686a3addbdb0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 3 Mar 2019 14:34:38 -0800 Subject: [PATCH 61/83] Infer higher order function types when possible --- src/compiler/checker.ts | 117 +++++++++++++++++++++++++++++++++++++--- src/compiler/types.ts | 1 + 2 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a8dccb072b1..c3375bb6536 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3983,7 +3983,7 @@ namespace ts { context.flags &= ~NodeBuilderFlags.WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic const shouldUseGeneratedName = context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && - type.symbol.declarations[0] && + type.symbol.declarations && type.symbol.declarations[0] && isTypeParameterDeclaration(type.symbol.declarations[0]) && typeParameterShadowsNameInScope(type, context); const name = shouldUseGeneratedName @@ -8375,9 +8375,23 @@ namespace ts { return undefined; } - function getSignatureInstantiation(signature: Signature, typeArguments: Type[] | undefined, isJavascript: boolean): Signature { - return getSignatureInstantiationWithoutFillingInTypeArguments(signature, fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript)); + function getSignatureInstantiation(signature: Signature, typeArguments: Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: ReadonlyArray): Signature { + const instantiatedSignature = getSignatureInstantiationWithoutFillingInTypeArguments(signature, fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript)); + if (inferredTypeParameters) { + const returnSignature = getSingleCallSignature(getReturnTypeOfSignature(instantiatedSignature)); + if (returnSignature) { + const newReturnSignature = cloneSignature(returnSignature); + newReturnSignature.typeParameters = inferredTypeParameters; + newReturnSignature.target = returnSignature.target; + newReturnSignature.mapper = returnSignature.mapper; + const newInstantiatedSignature = cloneSignature(instantiatedSignature); + newInstantiatedSignature.resolvedReturnType = getOrCreateTypeFromSignature(newReturnSignature); + return newInstantiatedSignature; + } + } + return instantiatedSignature; } + function getSignatureInstantiationWithoutFillingInTypeArguments(signature: Signature, typeArguments: ReadonlyArray | undefined): Signature { const instantiations = signature.instantiations || (signature.instantiations = createMap()); const id = getTypeListId(typeArguments); @@ -8391,6 +8405,7 @@ namespace ts { function createSignatureInstantiation(signature: Signature, typeArguments: ReadonlyArray | undefined): Signature { return instantiateSignature(signature, createSignatureTypeMapper(signature, typeArguments), /*eraseTypeParameters*/ true); } + function createSignatureTypeMapper(signature: Signature, typeArguments: ReadonlyArray | undefined): TypeMapper { return createTypeMapper(signature.typeParameters!, typeArguments); } @@ -20652,7 +20667,7 @@ namespace ts { inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext); } - checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration)); + checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); // If the original signature has a generic rest type, instantiation may produce a // signature with different arity and we need to perform another arity check. if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) { @@ -20677,7 +20692,7 @@ namespace ts { excludeArgument = undefined; if (inferenceContext) { const typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext); - checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration)); + checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); // If the original signature has a generic rest type, instantiation may produce a // signature with different arity and we need to perform another arity check. if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) { @@ -23378,7 +23393,35 @@ namespace ts { if (contextualType) { const contextualSignature = getSingleCallSignature(getNonNullableType(contextualType)); if (contextualSignature && !contextualSignature.typeParameters) { - return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, getContextualMapper(node))); + const context = getContextualMapper(node); + // We have an expression that is an argument of a generic function for which we are performing + // type argument inference. The expression is of a function type with a single generic call + // signature and a contextual function type with a single non-generic call signature. Now check + // if the outer function returns a function type with a single non-generic call signature and + // if some of the outer function type parameters have no inferences so far. If so, we can + // potentially add inferred type parameters to the outer function return type. + const returnSignature = context.signature && getSingleCallSignature(getReturnTypeOfSignature(context.signature)); + if (returnSignature && !returnSignature.typeParameters && !every(context.inferences, hasInferenceCandidates)) { + // Instantiate the expression type with its own type parameters as type arguments. This + // ensures that the type parameters are not erased to type any during type inference such + // that they can be inferred as actual types. + const uniqueTypeParameters = getUniqueTypeParameters(context, signature.typeParameters); + const strippedType = getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(signature, uniqueTypeParameters)); + // Infer from the stripped expression type to the contextual type starting with an empty + // set of inference candidates. + const inferences = map(context.typeParameters, createInferenceInfo); + inferTypes(inferences, strippedType, contextualType); + // If we produced some inference candidates and if the type parameters for which we produced + // candidates do not already have existing inferences, we adopt the new inference candidates and + // add the type parameters of the expression type to the set of inferred type parameters for + // the outer function return type. + if (some(inferences, hasInferenceCandidates) && !hasOverlappingInferences(context.inferences, inferences)) { + mergeInferences(context.inferences, inferences); + context.inferredTypeParameters = concatenate(context.inferredTypeParameters, uniqueTypeParameters); + return strippedType; + } + } + return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, context)); } } } @@ -23386,6 +23429,68 @@ namespace ts { return type; } + function hasInferenceCandidates(info: InferenceInfo) { + return !!(info.candidates || info.contraCandidates); + } + + function hasOverlappingInferences(a: InferenceInfo[], b: InferenceInfo[]) { + for (let i = 0; i < a.length; i++) { + if (hasInferenceCandidates(a[i]) && hasInferenceCandidates(b[i])) { + return true; + } + } + return false; + } + + function mergeInferences(target: InferenceInfo[], source: InferenceInfo[]) { + for (let i = 0; i < target.length; i++) { + if (!hasInferenceCandidates(target[i]) && hasInferenceCandidates(source[i])) { + target[i] = source[i]; + } + } + } + + function getUniqueTypeParameters(context: InferenceContext, typeParameters: ReadonlyArray): ReadonlyArray { + let result: TypeParameter[] = []; + let oldTypeParameters: TypeParameter[] | undefined; + let newTypeParameters: TypeParameter[] | undefined; + for (const tp of typeParameters) { + const name = tp.symbol.escapedName; + if (hasInferredTypeParameterByName(context, name)) { + const symbol = createSymbol(SymbolFlags.TypeParameter, getUniqueInferredTypeParameterName(context, name)); + const newTypeParameter = createTypeParameter(symbol); + newTypeParameter.target = tp; + oldTypeParameters = append(oldTypeParameters, tp); + newTypeParameters = append(newTypeParameters, newTypeParameter); + result.push(newTypeParameter); + } + else { + result.push(tp); + } + } + if (newTypeParameters) { + const mapper = createTypeMapper(oldTypeParameters!, newTypeParameters); + for (const tp of newTypeParameters) { + tp.mapper = mapper; + } + } + return result; + } + + function hasInferredTypeParameterByName(context: InferenceContext, name: __String) { + return some(context.inferredTypeParameters, tp => tp.symbol.escapedName === name); + } + + function getUniqueInferredTypeParameterName(context: InferenceContext, baseName: __String) { + let index = 1; + while (true) { + const augmentedName = <__String>(baseName + index); + if (!hasInferredTypeParameterByName(context, augmentedName)) { + return augmentedName; + } + } + } + /** * Returns the type of an expression. Unlike checkExpression, this function is simply concerned * with computing the type and may not fully check all contained sub-expressions for errors. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 42fd1126d4c..5217dd66fb4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4391,6 +4391,7 @@ namespace ts { flags: InferenceFlags; // Inference flags compareTypes: TypeComparer; // Type comparer function returnMapper?: TypeMapper; // Type mapper for inferences from return types (if any) + inferredTypeParameters?: ReadonlyArray; } /* @internal */ From c58819e9c2248294da8cbdc76ce14ae8d0388536 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 3 Mar 2019 17:01:11 -0800 Subject: [PATCH 62/83] Accept new baselines --- .../contextualSignatureInstantiation1.types | 8 +++--- .../contextualSignatureInstantiation2.types | 4 +-- .../reference/genericFunctionInference1.types | 26 +++++++++---------- .../genericTypeParameterEquivalence2.types | 20 +++++++------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/baselines/reference/contextualSignatureInstantiation1.types b/tests/baselines/reference/contextualSignatureInstantiation1.types index 82e0d7cb6fd..9bb1ff030ca 100644 --- a/tests/baselines/reference/contextualSignatureInstantiation1.types +++ b/tests/baselines/reference/contextualSignatureInstantiation1.types @@ -15,8 +15,8 @@ var e = (x: string, y?: K) => x.length; >length : number var r99 = map(e); // should be {}[] for S since a generic lambda is not inferentially typed ->r99 : (a: {}[]) => number[] ->map(e) : (a: {}[]) => number[] +>r99 : (a: string[]) => number[] +>map(e) : (a: string[]) => number[] >map : (f: (x: S) => T) => (a: S[]) => T[] >e : (x: string, y?: K) => number @@ -37,8 +37,8 @@ var e2 = (x: string, y?: K) => x.length; >length : number var r100 = map2(e2); // type arg inference should fail for S since a generic lambda is not inferentially typed. Falls back to { length: number } ->r100 : (a: { length: number; }[]) => number[] ->map2(e2) : (a: { length: number; }[]) => number[] +>r100 : (a: string[]) => number[] +>map2(e2) : (a: string[]) => number[] >map2 : (f: (x: S) => T) => (a: S[]) => T[] >e2 : (x: string, y?: K) => number diff --git a/tests/baselines/reference/contextualSignatureInstantiation2.types b/tests/baselines/reference/contextualSignatureInstantiation2.types index dbd526ab556..44776b5c01e 100644 --- a/tests/baselines/reference/contextualSignatureInstantiation2.types +++ b/tests/baselines/reference/contextualSignatureInstantiation2.types @@ -31,8 +31,8 @@ var id: (x:T) => T; >x : T var r23 = dot(id)(id); ->r23 : (_: {}) => {} ->dot(id)(id) : (_: {}) => {} +>r23 : (_: T) => {} +>dot(id)(id) : (_: T) => {} >dot(id) : (g: (_: U) => {}) => (_: U) => {} >dot : (f: (_: T) => S) => (g: (_: U) => T) => (_: U) => S >id : (x: T) => T diff --git a/tests/baselines/reference/genericFunctionInference1.types b/tests/baselines/reference/genericFunctionInference1.types index 037e4a49718..cf85c1b5afb 100644 --- a/tests/baselines/reference/genericFunctionInference1.types +++ b/tests/baselines/reference/genericFunctionInference1.types @@ -33,14 +33,14 @@ declare function box(x: V): { value: V }; >value : V const f00 = pipe(list); ->f00 : (a: any) => any[] ->pipe(list) : (a: any) => any[] +>f00 : (a: T) => T[] +>pipe(list) : (a: T) => T[] >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >list : (a: T) => T[] const f01 = pipe(list, box); ->f01 : (a: any) => { value: any[]; } ->pipe(list, box) : (a: any) => { value: any[]; } +>f01 : (a: T) => { value: T[]; } +>pipe(list, box) : (a: T) => { value: T[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >list : (a: T) => T[] >box : (x: V) => { value: V; } @@ -57,15 +57,15 @@ const f02 = pipe(x => list(x), box); >box : (x: V) => { value: V; } const f03 = pipe(list, x => box(x)); ->f03 : (a: any) => { value: any[]; } ->pipe(list, x => box(x)) : (a: any) => { value: any[]; } +>f03 : (a: T) => { value: T[]; } +>pipe(list, x => box(x)) : (a: T) => { value: T[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >list : (a: T) => T[] ->x => box(x) : (x: any[]) => { value: any[]; } ->x : any[] ->box(x) : { value: any[]; } +>x => box(x) : (x: T[]) => { value: T[]; } +>x : T[] +>box(x) : { value: T[]; } >box : (x: V) => { value: V; } ->x : any[] +>x : T[] const f04 = pipe(x => list(x), x => box(x)) >f04 : (x: any) => { value: any[]; } @@ -83,11 +83,11 @@ const f04 = pipe(x => list(x), x => box(x)) >x : any[] const f05 = pipe(list, pipe(box)); ->f05 : (a: any) => { value: any[]; } ->pipe(list, pipe(box)) : (a: any) => { value: any[]; } +>f05 : (a: T) => { value: T[]; } +>pipe(list, pipe(box)) : (a: T) => { value: T[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >list : (a: T) => T[] ->pipe(box) : (x: any[]) => { value: any[]; } +>pipe(box) : (x: T[]) => { value: T[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >box : (x: V) => { value: V; } diff --git a/tests/baselines/reference/genericTypeParameterEquivalence2.types b/tests/baselines/reference/genericTypeParameterEquivalence2.types index 1fafa5ad69d..63b9bb774f5 100644 --- a/tests/baselines/reference/genericTypeParameterEquivalence2.types +++ b/tests/baselines/reference/genericTypeParameterEquivalence2.types @@ -129,8 +129,8 @@ function curry1(f: (a: A, b: B) => C): (ax: A) => (bx: B) => C { } var cfilter = curry1(filter); ->cfilter : (ax: {}) => (bx: {}) => {}[] ->curry1(filter) : (ax: {}) => (bx: {}) => {}[] +>cfilter : (ax: (a: A) => boolean) => (bx: A[]) => A[] +>curry1(filter) : (ax: (a: A) => boolean) => (bx: A[]) => A[] >curry1 : (f: (a: A, b: B) => C) => (ax: A) => (bx: B) => C >filter : (f: (a: A) => boolean, ar: A[]) => A[] @@ -149,11 +149,11 @@ function countWhere_1(pred: (a: A) => boolean): (a: A[]) => number { >a : A[] return compose(length2, cfilter(pred)); ->compose(length2, cfilter(pred)) : (a: {}) => number +>compose(length2, cfilter(pred)) : (a: A[]) => number >compose : (f: (b: B) => C, g: (a: A) => B) => (a: A) => C >length2 : (ar: A[]) => number ->cfilter(pred) : (bx: {}) => {}[] ->cfilter : (ax: {}) => (bx: {}) => {}[] +>cfilter(pred) : (bx: A[]) => A[] +>cfilter : (ax: (a: A) => boolean) => (bx: A[]) => A[] >pred : (a: A) => boolean } @@ -164,14 +164,14 @@ function countWhere_2(pred: (a: A) => boolean): (a: A[]) => number { >a : A[] var where = cfilter(pred); ->where : (bx: {}) => {}[] ->cfilter(pred) : (bx: {}) => {}[] ->cfilter : (ax: {}) => (bx: {}) => {}[] +>where : (bx: A[]) => A[] +>cfilter(pred) : (bx: A[]) => A[] +>cfilter : (ax: (a: A) => boolean) => (bx: A[]) => A[] >pred : (a: A) => boolean return compose(length2, where); ->compose(length2, where) : (a: {}) => number +>compose(length2, where) : (a: A[]) => number >compose : (f: (b: B) => C, g: (a: A) => B) => (a: A) => C >length2 : (ar: A[]) => number ->where : (bx: {}) => {}[] +>where : (bx: A[]) => A[] } From 8c4ab69d09c9da1bb89dea46e2b31c6a846ae980 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 4 Mar 2019 09:34:04 -0800 Subject: [PATCH 63/83] add test for non-transient symbol --- ...onvertToNamedParameters_methodCallUnion.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertToNamedParameters_methodCallUnion.ts diff --git a/tests/cases/fourslash/refactorConvertToNamedParameters_methodCallUnion.ts b/tests/cases/fourslash/refactorConvertToNamedParameters_methodCallUnion.ts new file mode 100644 index 00000000000..e4cafbeff3e --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToNamedParameters_methodCallUnion.ts @@ -0,0 +1,29 @@ +/// + +////class A { +//// /*a*/foo/*b*/(a: number, b: number) { return a + b; } +////} +////class B { +//// foo(c: number, d: number) { return c + d; } +////} +////function foo(ab: A | B) { +//// return ab.foo(1, 2); +////} + + +goTo.select("a", "b"); +// Refactor should not make changes +edit.applyRefactor({ + refactorName: "Convert to named parameters", + actionName: "Convert to named parameters", + actionDescription: "Convert to named parameters", + newContent: `class A { + foo(a: number, b: number) { return a + b; } +} +class B { + foo(c: number, d: number) { return c + d; } +} +function foo(ab: A | B) { + return ab.foo(1, 2); +}` +}); \ No newline at end of file From 919ade1dbd3f7e6acfc71b02defb622c0bcb8d43 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 4 Mar 2019 10:57:08 -0800 Subject: [PATCH 64/83] Improve scheme for creating unique type parameter names --- src/compiler/checker.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c3375bb6536..d97abd4a5d0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23456,8 +23456,9 @@ namespace ts { let newTypeParameters: TypeParameter[] | undefined; for (const tp of typeParameters) { const name = tp.symbol.escapedName; - if (hasInferredTypeParameterByName(context, name)) { - const symbol = createSymbol(SymbolFlags.TypeParameter, getUniqueInferredTypeParameterName(context, name)); + if (hasTypeParameterByName(context.inferredTypeParameters, name) || hasTypeParameterByName(result, name)) { + const newName = getUniqueTypeParameterName(concatenate(context.inferredTypeParameters, result), name); + const symbol = createSymbol(SymbolFlags.TypeParameter, newName); const newTypeParameter = createTypeParameter(symbol); newTypeParameter.target = tp; oldTypeParameters = append(oldTypeParameters, tp); @@ -23477,15 +23478,17 @@ namespace ts { return result; } - function hasInferredTypeParameterByName(context: InferenceContext, name: __String) { - return some(context.inferredTypeParameters, tp => tp.symbol.escapedName === name); + function hasTypeParameterByName(typeParameters: ReadonlyArray | undefined, name: __String) { + return some(typeParameters, tp => tp.symbol.escapedName === name); } - function getUniqueInferredTypeParameterName(context: InferenceContext, baseName: __String) { - let index = 1; - while (true) { - const augmentedName = <__String>(baseName + index); - if (!hasInferredTypeParameterByName(context, augmentedName)) { + function getUniqueTypeParameterName(typeParameters: ReadonlyArray, baseName: __String) { + let len = (baseName).length; + while (len > 1 && (baseName).charCodeAt(len - 1) >= CharacterCodes._0 && (baseName).charCodeAt(len - 1) <= CharacterCodes._9) len--; + const s = (baseName).slice(0, len); + for (let index = 1; true; index++) { + const augmentedName = <__String>(s + index); + if (!hasTypeParameterByName(typeParameters, augmentedName)) { return augmentedName; } } From 549c68451708a9c48b2127ba6bc6618354366fdd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 4 Mar 2019 11:02:55 -0800 Subject: [PATCH 65/83] Minor fix --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d97abd4a5d0..9ca8b60d36a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22104,7 +22104,7 @@ namespace ts { forEachReturnStatement(func.body, returnStatement => { const expr = returnStatement.expression; if (expr) { - let type = checkExpressionCached(expr, checkMode && checkMode && checkMode & ~CheckMode.SkipGenericFunctions); + let type = checkExpressionCached(expr, checkMode && checkMode & ~CheckMode.SkipGenericFunctions); if (functionFlags & FunctionFlags.Async) { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the From 22c934a065d56f0578a58b6258fcc8775ffd4cf0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 4 Mar 2019 11:07:02 -0800 Subject: [PATCH 66/83] Add tests --- .../compiler/genericFunctionInference1.ts | 133 ++++++++++++++++-- 1 file changed, 119 insertions(+), 14 deletions(-) diff --git a/tests/cases/compiler/genericFunctionInference1.ts b/tests/cases/compiler/genericFunctionInference1.ts index c5416f32bda..f2a71ca7f02 100644 --- a/tests/cases/compiler/genericFunctionInference1.ts +++ b/tests/cases/compiler/genericFunctionInference1.ts @@ -7,24 +7,129 @@ declare function pipe(ab: (...args: A) => B, bc: (b: B declare function list(a: T): T[]; declare function box(x: V): { value: V }; +declare function foo(x: T): T; const f00 = pipe(list); const f01 = pipe(list, box); -const f02 = pipe(x => list(x), box); -const f03 = pipe(list, x => box(x)); -const f04 = pipe(x => list(x), x => box(x)) -const f05 = pipe(list, pipe(box)); -const f06 = pipe(x => list(x), pipe(box)); -const f07 = pipe(x => list(x), pipe(x => box(x))); +const f02 = pipe(box, list); +const f03 = pipe(x => list(x), box); +const f04 = pipe(list, x => box(x)); +const f05 = pipe(x => list(x), x => box(x)) +const f06 = pipe(list, pipe(box)); +const f07 = pipe(x => list(x), pipe(box)); +const f08 = pipe(x => list(x), pipe(x => box(x))); +const f09 = pipe(list, x => x.length); +const f10 = pipe(foo); +const f11 = pipe(foo, foo); -const f10: (x: T) => T[] = pipe(list); -const f11: (x: T) => { value: T[] } = pipe(list, box); -const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); -const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); -const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) -const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); -const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); -const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); +const g00: (x: T) => T[] = pipe(list); +const g01: (x: T) => { value: T[] } = pipe(list, box); +const g02: (x: T) => { value: T }[] = pipe(box, list); +const g03: (x: T) => { value: T[] } = pipe(x => list(x), box); +const g04: (x: T) => { value: T[] } = pipe(list, x => box(x)); +const g05: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) +const g06: (x: T) => { value: T[] } = pipe(list, pipe(box)); +const g07: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); +const g08: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); +const g09: (x: T) => number = pipe(list, x => x.length); +const g10: (x: T) => T = pipe(foo); +const g12: (x: T) => T = pipe(foo, foo); + +declare function pipe2(ab: (a: A) => B, cd: (c: C) => D): (a: [A, C]) => [B, D]; + +const f20 = pipe2(list, box); +const f21 = pipe2(box, list); +const f22 = pipe2(list, list); +const f23 = pipe2(box, box); +const f24 = pipe2(f20, f20); +const f25 = pipe2(foo, foo); +const f26 = pipe2(f25, f25); + +declare function pipe3(ab: (a: A) => B, ac: (a: A) => C): (a: A) => [B, C]; + +const f30 = pipe3(list, box); +const f31 = pipe3(box, list); +const f32 = pipe3(list, list); + +declare function pipe4(funcs: [(a: A) => B, (b: B) => C]): (a: A) => C; + +const f40 = pipe4([list, box]); +const f41 = pipe4([box, list]); + +declare function pipe5(f: (a: A) => B): { f: (a: A) => B }; + +const f50 = pipe5(list); // No higher order inference + +// #417 + +function mirror(f: (a: A) => B): (a: A) => B { return f; } +var identityM = mirror(identity); + +var x = 1; +var y = identity(x); +var z = identityM(x); + +// #3038 + +export function keyOf(value: { key: a; }): a { + return value.key; +} +export interface Data { + key: number; + value: Date; +} + +var data: Data[] = []; + +declare function toKeys(values: a[], toKey: (value: a) => string): string[]; + +toKeys(data, keyOf); // Error + +// #9366 + +function flip(f: (a: a, b: b) => c): (b: b, a: a) => c { + return (b: b, a: a) => f(a, b); +} +function zip(x: T, y: U): [T, U] { + return [x, y]; +} + +var expected: (y: U, x: T) => [T, U] = flip(zip); +var actual = flip(zip); + +// #9366 + +const map = (transform: (t: T) => U) => + (arr: T[]) => arr.map(transform) + +const identityStr = (t: string) => t; + +const arr: string[] = map(identityStr)(['a']); +const arr1: string[] = map(identity)(['a']); + +// #9949 + +function of2(one: a, two: b): [a, b] { + return [one, two]; +} + +const flipped = flip(of2); + +// #29904.1 + +type Component

= (props: P) => {}; + +declare const myHoc1:

(C: Component

) => Component

; +declare const myHoc2:

(C: Component

) => Component

; + +declare const MyComponent1: Component<{ foo: 1 }>; + +const enhance = pipe( + myHoc1, + myHoc2, +); + +const MyComponent2 = enhance(MyComponent1); // #29904.2 From 5fe8ebb502f90742a0c53b20d7bc38439ea59d70 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 4 Mar 2019 11:07:11 -0800 Subject: [PATCH 67/83] Accept new baselines --- .../genericFunctionInference1.errors.txt | 193 +++++ .../reference/genericFunctionInference1.js | 216 ++++- .../genericFunctionInference1.symbols | 771 ++++++++++++++---- .../reference/genericFunctionInference1.types | 460 ++++++++++- 4 files changed, 1432 insertions(+), 208 deletions(-) create mode 100644 tests/baselines/reference/genericFunctionInference1.errors.txt diff --git a/tests/baselines/reference/genericFunctionInference1.errors.txt b/tests/baselines/reference/genericFunctionInference1.errors.txt new file mode 100644 index 00000000000..9b082d39ab2 --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference1.errors.txt @@ -0,0 +1,193 @@ +tests/cases/compiler/genericFunctionInference1.ts(83,14): error TS2345: Argument of type '(value: { key: a; }) => a' is not assignable to parameter of type '(value: Data) => string'. + Type 'number' is not assignable to type 'string'. + + +==== tests/cases/compiler/genericFunctionInference1.ts (1 errors) ==== + declare function pipe(ab: (...args: A) => B): (...args: A) => B; + declare function pipe(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; + declare function pipe(ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; + + declare function list(a: T): T[]; + declare function box(x: V): { value: V }; + declare function foo(x: T): T; + + const f00 = pipe(list); + const f01 = pipe(list, box); + const f02 = pipe(box, list); + const f03 = pipe(x => list(x), box); + const f04 = pipe(list, x => box(x)); + const f05 = pipe(x => list(x), x => box(x)) + const f06 = pipe(list, pipe(box)); + const f07 = pipe(x => list(x), pipe(box)); + const f08 = pipe(x => list(x), pipe(x => box(x))); + const f09 = pipe(list, x => x.length); + const f10 = pipe(foo); + const f11 = pipe(foo, foo); + + const g00: (x: T) => T[] = pipe(list); + const g01: (x: T) => { value: T[] } = pipe(list, box); + const g02: (x: T) => { value: T }[] = pipe(box, list); + const g03: (x: T) => { value: T[] } = pipe(x => list(x), box); + const g04: (x: T) => { value: T[] } = pipe(list, x => box(x)); + const g05: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) + const g06: (x: T) => { value: T[] } = pipe(list, pipe(box)); + const g07: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); + const g08: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); + const g09: (x: T) => number = pipe(list, x => x.length); + const g10: (x: T) => T = pipe(foo); + const g12: (x: T) => T = pipe(foo, foo); + + declare function pipe2(ab: (a: A) => B, cd: (c: C) => D): (a: [A, C]) => [B, D]; + + const f20 = pipe2(list, box); + const f21 = pipe2(box, list); + const f22 = pipe2(list, list); + const f23 = pipe2(box, box); + const f24 = pipe2(f20, f20); + const f25 = pipe2(foo, foo); + const f26 = pipe2(f25, f25); + + declare function pipe3(ab: (a: A) => B, ac: (a: A) => C): (a: A) => [B, C]; + + const f30 = pipe3(list, box); + const f31 = pipe3(box, list); + const f32 = pipe3(list, list); + + declare function pipe4(funcs: [(a: A) => B, (b: B) => C]): (a: A) => C; + + const f40 = pipe4([list, box]); + const f41 = pipe4([box, list]); + + declare function pipe5(f: (a: A) => B): { f: (a: A) => B }; + + const f50 = pipe5(list); // No higher order inference + + // #417 + + function mirror(f: (a: A) => B): (a: A) => B { return f; } + var identityM = mirror(identity); + + var x = 1; + var y = identity(x); + var z = identityM(x); + + // #3038 + + export function keyOf(value: { key: a; }): a { + return value.key; + } + export interface Data { + key: number; + value: Date; + } + + var data: Data[] = []; + + declare function toKeys(values: a[], toKey: (value: a) => string): string[]; + + toKeys(data, keyOf); // Error + ~~~~~ +!!! error TS2345: Argument of type '(value: { key: a; }) => a' is not assignable to parameter of type '(value: Data) => string'. +!!! error TS2345: Type 'number' is not assignable to type 'string'. + + // #9366 + + function flip(f: (a: a, b: b) => c): (b: b, a: a) => c { + return (b: b, a: a) => f(a, b); + } + function zip(x: T, y: U): [T, U] { + return [x, y]; + } + + var expected: (y: U, x: T) => [T, U] = flip(zip); + var actual = flip(zip); + + // #9366 + + const map = (transform: (t: T) => U) => + (arr: T[]) => arr.map(transform) + + const identityStr = (t: string) => t; + + const arr: string[] = map(identityStr)(['a']); + const arr1: string[] = map(identity)(['a']); + + // #9949 + + function of2(one: a, two: b): [a, b] { + return [one, two]; + } + + const flipped = flip(of2); + + // #29904.1 + + type Component

= (props: P) => {}; + + declare const myHoc1:

(C: Component

) => Component

; + declare const myHoc2:

(C: Component

) => Component

; + + declare const MyComponent1: Component<{ foo: 1 }>; + + const enhance = pipe( + myHoc1, + myHoc2, + ); + + const MyComponent2 = enhance(MyComponent1); + + // #29904.2 + + const fn20 = pipe((_a?: {}) => 1); + + // #29904.3 + + type Fn = (n: number) => number; + const fn30: Fn = pipe( + x => x + 1, + x => x * 2, + ); + + const promise = Promise.resolve(1); + promise.then( + pipe( + x => x + 1, + x => x * 2, + ), + ); + + // #29904.4 + + declare const getString: () => string; + declare const orUndefined: (name: string) => string | undefined; + declare const identity: (value: T) => T; + + const fn40 = pipe( + getString, + string => orUndefined(string), + identity, + ); + + // #29904.6 + + declare const getArray: () => string[]; + declare const first: (ts: T[]) => T; + + const fn60 = pipe( + getArray, + x => x, + first, + ); + + const fn61 = pipe( + getArray, + identity, + first, + ); + + const fn62 = pipe( + getArray, + x => x, + x => first(x), + ); + \ No newline at end of file diff --git a/tests/baselines/reference/genericFunctionInference1.js b/tests/baselines/reference/genericFunctionInference1.js index 872656ba9c5..4e89f9c0795 100644 --- a/tests/baselines/reference/genericFunctionInference1.js +++ b/tests/baselines/reference/genericFunctionInference1.js @@ -5,24 +5,129 @@ declare function pipe(ab: (...args: A) => B, bc: (b: B declare function list(a: T): T[]; declare function box(x: V): { value: V }; +declare function foo(x: T): T; const f00 = pipe(list); const f01 = pipe(list, box); -const f02 = pipe(x => list(x), box); -const f03 = pipe(list, x => box(x)); -const f04 = pipe(x => list(x), x => box(x)) -const f05 = pipe(list, pipe(box)); -const f06 = pipe(x => list(x), pipe(box)); -const f07 = pipe(x => list(x), pipe(x => box(x))); +const f02 = pipe(box, list); +const f03 = pipe(x => list(x), box); +const f04 = pipe(list, x => box(x)); +const f05 = pipe(x => list(x), x => box(x)) +const f06 = pipe(list, pipe(box)); +const f07 = pipe(x => list(x), pipe(box)); +const f08 = pipe(x => list(x), pipe(x => box(x))); +const f09 = pipe(list, x => x.length); +const f10 = pipe(foo); +const f11 = pipe(foo, foo); -const f10: (x: T) => T[] = pipe(list); -const f11: (x: T) => { value: T[] } = pipe(list, box); -const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); -const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); -const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) -const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); -const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); -const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); +const g00: (x: T) => T[] = pipe(list); +const g01: (x: T) => { value: T[] } = pipe(list, box); +const g02: (x: T) => { value: T }[] = pipe(box, list); +const g03: (x: T) => { value: T[] } = pipe(x => list(x), box); +const g04: (x: T) => { value: T[] } = pipe(list, x => box(x)); +const g05: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) +const g06: (x: T) => { value: T[] } = pipe(list, pipe(box)); +const g07: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); +const g08: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); +const g09: (x: T) => number = pipe(list, x => x.length); +const g10: (x: T) => T = pipe(foo); +const g12: (x: T) => T = pipe(foo, foo); + +declare function pipe2(ab: (a: A) => B, cd: (c: C) => D): (a: [A, C]) => [B, D]; + +const f20 = pipe2(list, box); +const f21 = pipe2(box, list); +const f22 = pipe2(list, list); +const f23 = pipe2(box, box); +const f24 = pipe2(f20, f20); +const f25 = pipe2(foo, foo); +const f26 = pipe2(f25, f25); + +declare function pipe3(ab: (a: A) => B, ac: (a: A) => C): (a: A) => [B, C]; + +const f30 = pipe3(list, box); +const f31 = pipe3(box, list); +const f32 = pipe3(list, list); + +declare function pipe4(funcs: [(a: A) => B, (b: B) => C]): (a: A) => C; + +const f40 = pipe4([list, box]); +const f41 = pipe4([box, list]); + +declare function pipe5(f: (a: A) => B): { f: (a: A) => B }; + +const f50 = pipe5(list); // No higher order inference + +// #417 + +function mirror(f: (a: A) => B): (a: A) => B { return f; } +var identityM = mirror(identity); + +var x = 1; +var y = identity(x); +var z = identityM(x); + +// #3038 + +export function keyOf(value: { key: a; }): a { + return value.key; +} +export interface Data { + key: number; + value: Date; +} + +var data: Data[] = []; + +declare function toKeys(values: a[], toKey: (value: a) => string): string[]; + +toKeys(data, keyOf); // Error + +// #9366 + +function flip(f: (a: a, b: b) => c): (b: b, a: a) => c { + return (b: b, a: a) => f(a, b); +} +function zip(x: T, y: U): [T, U] { + return [x, y]; +} + +var expected: (y: U, x: T) => [T, U] = flip(zip); +var actual = flip(zip); + +// #9366 + +const map = (transform: (t: T) => U) => + (arr: T[]) => arr.map(transform) + +const identityStr = (t: string) => t; + +const arr: string[] = map(identityStr)(['a']); +const arr1: string[] = map(identity)(['a']); + +// #9949 + +function of2(one: a, two: b): [a, b] { + return [one, two]; +} + +const flipped = flip(of2); + +// #29904.1 + +type Component

= (props: P) => {}; + +declare const myHoc1:

(C: Component

) => Component

; +declare const myHoc2:

(C: Component

) => Component

; + +declare const MyComponent1: Component<{ foo: 1 }>; + +const enhance = pipe( + myHoc1, + myHoc2, +); + +const MyComponent2 = enhance(MyComponent1); // #29904.2 @@ -81,23 +186,76 @@ const fn62 = pipe( //// [genericFunctionInference1.js] -"use strict"; const f00 = pipe(list); const f01 = pipe(list, box); -const f02 = pipe(x => list(x), box); -const f03 = pipe(list, x => box(x)); -const f04 = pipe(x => list(x), x => box(x)); -const f05 = pipe(list, pipe(box)); -const f06 = pipe(x => list(x), pipe(box)); -const f07 = pipe(x => list(x), pipe(x => box(x))); -const f10 = pipe(list); -const f11 = pipe(list, box); -const f12 = pipe(x => list(x), box); -const f13 = pipe(list, x => box(x)); -const f14 = pipe(x => list(x), x => box(x)); -const f15 = pipe(list, pipe(box)); -const f16 = pipe(x => list(x), pipe(box)); -const f17 = pipe(x => list(x), pipe(x => box(x))); +const f02 = pipe(box, list); +const f03 = pipe(x => list(x), box); +const f04 = pipe(list, x => box(x)); +const f05 = pipe(x => list(x), x => box(x)); +const f06 = pipe(list, pipe(box)); +const f07 = pipe(x => list(x), pipe(box)); +const f08 = pipe(x => list(x), pipe(x => box(x))); +const f09 = pipe(list, x => x.length); +const f10 = pipe(foo); +const f11 = pipe(foo, foo); +const g00 = pipe(list); +const g01 = pipe(list, box); +const g02 = pipe(box, list); +const g03 = pipe(x => list(x), box); +const g04 = pipe(list, x => box(x)); +const g05 = pipe(x => list(x), x => box(x)); +const g06 = pipe(list, pipe(box)); +const g07 = pipe(x => list(x), pipe(box)); +const g08 = pipe(x => list(x), pipe(x => box(x))); +const g09 = pipe(list, x => x.length); +const g10 = pipe(foo); +const g12 = pipe(foo, foo); +const f20 = pipe2(list, box); +const f21 = pipe2(box, list); +const f22 = pipe2(list, list); +const f23 = pipe2(box, box); +const f24 = pipe2(f20, f20); +const f25 = pipe2(foo, foo); +const f26 = pipe2(f25, f25); +const f30 = pipe3(list, box); +const f31 = pipe3(box, list); +const f32 = pipe3(list, list); +const f40 = pipe4([list, box]); +const f41 = pipe4([box, list]); +const f50 = pipe5(list); // No higher order inference +// #417 +function mirror(f) { return f; } +var identityM = mirror(identity); +var x = 1; +var y = identity(x); +var z = identityM(x); +// #3038 +export function keyOf(value) { + return value.key; +} +var data = []; +toKeys(data, keyOf); // Error +// #9366 +function flip(f) { + return (b, a) => f(a, b); +} +function zip(x, y) { + return [x, y]; +} +var expected = flip(zip); +var actual = flip(zip); +// #9366 +const map = (transform) => (arr) => arr.map(transform); +const identityStr = (t) => t; +const arr = map(identityStr)(['a']); +const arr1 = map(identity)(['a']); +// #9949 +function of2(one, two) { + return [one, two]; +} +const flipped = flip(of2); +const enhance = pipe(myHoc1, myHoc2); +const MyComponent2 = enhance(MyComponent1); // #29904.2 const fn20 = pipe((_a) => 1); const fn30 = pipe(x => x + 1, x => x * 2); diff --git a/tests/baselines/reference/genericFunctionInference1.symbols b/tests/baselines/reference/genericFunctionInference1.symbols index d49a20cec9c..9ee085feae8 100644 --- a/tests/baselines/reference/genericFunctionInference1.symbols +++ b/tests/baselines/reference/genericFunctionInference1.symbols @@ -65,222 +65,691 @@ declare function box(x: V): { value: V }; >value : Symbol(value, Decl(genericFunctionInference1.ts, 5, 32)) >V : Symbol(V, Decl(genericFunctionInference1.ts, 5, 21)) +declare function foo(x: T): T; +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 6, 21)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 6, 32)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 6, 21)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 6, 45)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 6, 21)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 6, 21)) + const f00 = pipe(list); ->f00 : Symbol(f00, Decl(genericFunctionInference1.ts, 7, 5)) +>f00 : Symbol(f00, Decl(genericFunctionInference1.ts, 8, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) const f01 = pipe(list, box); ->f01 : Symbol(f01, Decl(genericFunctionInference1.ts, 8, 5)) +>f01 : Symbol(f01, Decl(genericFunctionInference1.ts, 9, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) -const f02 = pipe(x => list(x), box); ->f02 : Symbol(f02, Decl(genericFunctionInference1.ts, 9, 5)) +const f02 = pipe(box, list); +>f02 : Symbol(f02, Decl(genericFunctionInference1.ts, 10, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 9, 17)) ->list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 9, 17)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) - -const f03 = pipe(list, x => box(x)); ->f03 : Symbol(f03, Decl(genericFunctionInference1.ts, 10, 5)) ->pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 10, 22)) ->box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 10, 22)) -const f04 = pipe(x => list(x), x => box(x)) ->f04 : Symbol(f04, Decl(genericFunctionInference1.ts, 11, 5)) +const f03 = pipe(x => list(x), box); +>f03 : Symbol(f03, Decl(genericFunctionInference1.ts, 11, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >x : Symbol(x, Decl(genericFunctionInference1.ts, 11, 17)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) >x : Symbol(x, Decl(genericFunctionInference1.ts, 11, 17)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 11, 30)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 11, 30)) -const f05 = pipe(list, pipe(box)); ->f05 : Symbol(f05, Decl(genericFunctionInference1.ts, 12, 5)) +const f04 = pipe(list, x => box(x)); +>f04 : Symbol(f04, Decl(genericFunctionInference1.ts, 12, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 12, 22)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 12, 22)) -const f06 = pipe(x => list(x), pipe(box)); ->f06 : Symbol(f06, Decl(genericFunctionInference1.ts, 13, 5)) +const f05 = pipe(x => list(x), x => box(x)) +>f05 : Symbol(f05, Decl(genericFunctionInference1.ts, 13, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >x : Symbol(x, Decl(genericFunctionInference1.ts, 13, 17)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) >x : Symbol(x, Decl(genericFunctionInference1.ts, 13, 17)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 13, 30)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 13, 30)) + +const f06 = pipe(list, pipe(box)); +>f06 : Symbol(f06, Decl(genericFunctionInference1.ts, 14, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) -const f07 = pipe(x => list(x), pipe(x => box(x))); ->f07 : Symbol(f07, Decl(genericFunctionInference1.ts, 14, 5)) +const f07 = pipe(x => list(x), pipe(box)); +>f07 : Symbol(f07, Decl(genericFunctionInference1.ts, 15, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 14, 17)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 15, 17)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 14, 17)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 15, 17)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 14, 36)) ->box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 14, 36)) - -const f10: (x: T) => T[] = pipe(list); ->f10 : Symbol(f10, Decl(genericFunctionInference1.ts, 16, 5)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 16, 12)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 16, 15)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 16, 12)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 16, 12)) ->pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) - -const f11: (x: T) => { value: T[] } = pipe(list, box); ->f11 : Symbol(f11, Decl(genericFunctionInference1.ts, 17, 5)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 17, 12)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 17, 15)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 17, 12)) ->value : Symbol(value, Decl(genericFunctionInference1.ts, 17, 25)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 17, 12)) ->pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) -const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); ->f12 : Symbol(f12, Decl(genericFunctionInference1.ts, 18, 5)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 18, 12)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 18, 15)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 18, 12)) ->value : Symbol(value, Decl(genericFunctionInference1.ts, 18, 25)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 18, 12)) +const f08 = pipe(x => list(x), pipe(x => box(x))); +>f08 : Symbol(f08, Decl(genericFunctionInference1.ts, 16, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 18, 46)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 16, 17)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 18, 46)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 16, 17)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 16, 36)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 16, 36)) -const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); ->f13 : Symbol(f13, Decl(genericFunctionInference1.ts, 19, 5)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 19, 12)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 19, 15)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 19, 12)) ->value : Symbol(value, Decl(genericFunctionInference1.ts, 19, 25)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 19, 12)) +const f09 = pipe(list, x => x.length); +>f09 : Symbol(f09, Decl(genericFunctionInference1.ts, 17, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 19, 51)) ->box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 19, 51)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 17, 22)) +>x.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 17, 22)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) -const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) ->f14 : Symbol(f14, Decl(genericFunctionInference1.ts, 20, 5)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 20, 12)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 15)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 20, 12)) ->value : Symbol(value, Decl(genericFunctionInference1.ts, 20, 25)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 20, 12)) +const f10 = pipe(foo); +>f10 : Symbol(f10, Decl(genericFunctionInference1.ts, 18, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 46)) ->list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 46)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 59)) ->box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 20, 59)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) -const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); ->f15 : Symbol(f15, Decl(genericFunctionInference1.ts, 21, 5)) +const f11 = pipe(foo, foo); +>f11 : Symbol(f11, Decl(genericFunctionInference1.ts, 19, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) + +const g00: (x: T) => T[] = pipe(list); +>g00 : Symbol(g00, Decl(genericFunctionInference1.ts, 21, 5)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 21, 12)) >x : Symbol(x, Decl(genericFunctionInference1.ts, 21, 15)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 21, 12)) ->value : Symbol(value, Decl(genericFunctionInference1.ts, 21, 25)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 21, 12)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) -const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); ->f16 : Symbol(f16, Decl(genericFunctionInference1.ts, 22, 5)) +const g01: (x: T) => { value: T[] } = pipe(list, box); +>g01 : Symbol(g01, Decl(genericFunctionInference1.ts, 22, 5)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 22, 12)) >x : Symbol(x, Decl(genericFunctionInference1.ts, 22, 15)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 22, 12)) >value : Symbol(value, Decl(genericFunctionInference1.ts, 22, 25)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 22, 12)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 22, 46)) >list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 22, 46)) ->pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) -const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); ->f17 : Symbol(f17, Decl(genericFunctionInference1.ts, 23, 5)) +const g02: (x: T) => { value: T }[] = pipe(box, list); +>g02 : Symbol(g02, Decl(genericFunctionInference1.ts, 23, 5)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 23, 12)) >x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 15)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 23, 12)) >value : Symbol(value, Decl(genericFunctionInference1.ts, 23, 25)) >T : Symbol(T, Decl(genericFunctionInference1.ts, 23, 12)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 46)) ->list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 46)) ->pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 65)) >box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 23, 65)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +const g03: (x: T) => { value: T[] } = pipe(x => list(x), box); +>g03 : Symbol(g03, Decl(genericFunctionInference1.ts, 24, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 24, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 24, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 24, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 24, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 24, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 24, 46)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 24, 46)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const g04: (x: T) => { value: T[] } = pipe(list, x => box(x)); +>g04 : Symbol(g04, Decl(genericFunctionInference1.ts, 25, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 25, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 25, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 25, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 25, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 25, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 25, 51)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 25, 51)) + +const g05: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) +>g05 : Symbol(g05, Decl(genericFunctionInference1.ts, 26, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 26, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 26, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 26, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 26, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 26, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 26, 46)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 26, 46)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 26, 59)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 26, 59)) + +const g06: (x: T) => { value: T[] } = pipe(list, pipe(box)); +>g06 : Symbol(g06, Decl(genericFunctionInference1.ts, 27, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 27, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 27, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 27, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 27, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 27, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const g07: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); +>g07 : Symbol(g07, Decl(genericFunctionInference1.ts, 28, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 28, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 28, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 28, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 28, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 28, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 28, 46)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 28, 46)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const g08: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); +>g08 : Symbol(g08, Decl(genericFunctionInference1.ts, 29, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 29, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 29, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 29, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 29, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 29, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 29, 46)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 29, 46)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 29, 65)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 29, 65)) + +const g09: (x: T) => number = pipe(list, x => x.length); +>g09 : Symbol(g09, Decl(genericFunctionInference1.ts, 30, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 30, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 30, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 30, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 30, 43)) +>x.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 30, 43)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) + +const g10: (x: T) => T = pipe(foo); +>g10 : Symbol(g10, Decl(genericFunctionInference1.ts, 31, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 31, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 31, 23)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 31, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 31, 36)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 31, 12)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 31, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) + +const g12: (x: T) => T = pipe(foo, foo); +>g12 : Symbol(g12, Decl(genericFunctionInference1.ts, 32, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 32, 12)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 32, 23)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 32, 12)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 32, 36)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 32, 12)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 32, 12)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) + +declare function pipe2(ab: (a: A) => B, cd: (c: C) => D): (a: [A, C]) => [B, D]; +>pipe2 : Symbol(pipe2, Decl(genericFunctionInference1.ts, 32, 64)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 34, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 34, 25)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 34, 28)) +>D : Symbol(D, Decl(genericFunctionInference1.ts, 34, 31)) +>ab : Symbol(ab, Decl(genericFunctionInference1.ts, 34, 35)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 34, 40)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 34, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 34, 25)) +>cd : Symbol(cd, Decl(genericFunctionInference1.ts, 34, 51)) +>c : Symbol(c, Decl(genericFunctionInference1.ts, 34, 57)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 34, 28)) +>D : Symbol(D, Decl(genericFunctionInference1.ts, 34, 31)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 34, 71)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 34, 23)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 34, 28)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 34, 25)) +>D : Symbol(D, Decl(genericFunctionInference1.ts, 34, 31)) + +const f20 = pipe2(list, box); +>f20 : Symbol(f20, Decl(genericFunctionInference1.ts, 36, 5)) +>pipe2 : Symbol(pipe2, Decl(genericFunctionInference1.ts, 32, 64)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f21 = pipe2(box, list); +>f21 : Symbol(f21, Decl(genericFunctionInference1.ts, 37, 5)) +>pipe2 : Symbol(pipe2, Decl(genericFunctionInference1.ts, 32, 64)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +const f22 = pipe2(list, list); +>f22 : Symbol(f22, Decl(genericFunctionInference1.ts, 38, 5)) +>pipe2 : Symbol(pipe2, Decl(genericFunctionInference1.ts, 32, 64)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +const f23 = pipe2(box, box); +>f23 : Symbol(f23, Decl(genericFunctionInference1.ts, 39, 5)) +>pipe2 : Symbol(pipe2, Decl(genericFunctionInference1.ts, 32, 64)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f24 = pipe2(f20, f20); +>f24 : Symbol(f24, Decl(genericFunctionInference1.ts, 40, 5)) +>pipe2 : Symbol(pipe2, Decl(genericFunctionInference1.ts, 32, 64)) +>f20 : Symbol(f20, Decl(genericFunctionInference1.ts, 36, 5)) +>f20 : Symbol(f20, Decl(genericFunctionInference1.ts, 36, 5)) + +const f25 = pipe2(foo, foo); +>f25 : Symbol(f25, Decl(genericFunctionInference1.ts, 41, 5)) +>pipe2 : Symbol(pipe2, Decl(genericFunctionInference1.ts, 32, 64)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 5, 44)) + +const f26 = pipe2(f25, f25); +>f26 : Symbol(f26, Decl(genericFunctionInference1.ts, 42, 5)) +>pipe2 : Symbol(pipe2, Decl(genericFunctionInference1.ts, 32, 64)) +>f25 : Symbol(f25, Decl(genericFunctionInference1.ts, 41, 5)) +>f25 : Symbol(f25, Decl(genericFunctionInference1.ts, 41, 5)) + +declare function pipe3(ab: (a: A) => B, ac: (a: A) => C): (a: A) => [B, C]; +>pipe3 : Symbol(pipe3, Decl(genericFunctionInference1.ts, 42, 28)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 44, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 44, 25)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 44, 28)) +>ab : Symbol(ab, Decl(genericFunctionInference1.ts, 44, 32)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 44, 37)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 44, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 44, 25)) +>ac : Symbol(ac, Decl(genericFunctionInference1.ts, 44, 48)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 44, 54)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 44, 23)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 44, 28)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 44, 68)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 44, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 44, 25)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 44, 28)) + +const f30 = pipe3(list, box); +>f30 : Symbol(f30, Decl(genericFunctionInference1.ts, 46, 5)) +>pipe3 : Symbol(pipe3, Decl(genericFunctionInference1.ts, 42, 28)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f31 = pipe3(box, list); +>f31 : Symbol(f31, Decl(genericFunctionInference1.ts, 47, 5)) +>pipe3 : Symbol(pipe3, Decl(genericFunctionInference1.ts, 42, 28)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +const f32 = pipe3(list, list); +>f32 : Symbol(f32, Decl(genericFunctionInference1.ts, 48, 5)) +>pipe3 : Symbol(pipe3, Decl(genericFunctionInference1.ts, 42, 28)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +declare function pipe4(funcs: [(a: A) => B, (b: B) => C]): (a: A) => C; +>pipe4 : Symbol(pipe4, Decl(genericFunctionInference1.ts, 48, 30)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 50, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 50, 25)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 50, 28)) +>funcs : Symbol(funcs, Decl(genericFunctionInference1.ts, 50, 32)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 50, 41)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 50, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 50, 25)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 50, 54)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 50, 25)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 50, 28)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 50, 69)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 50, 23)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 50, 28)) + +const f40 = pipe4([list, box]); +>f40 : Symbol(f40, Decl(genericFunctionInference1.ts, 52, 5)) +>pipe4 : Symbol(pipe4, Decl(genericFunctionInference1.ts, 48, 30)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) + +const f41 = pipe4([box, list]); +>f41 : Symbol(f41, Decl(genericFunctionInference1.ts, 53, 5)) +>pipe4 : Symbol(pipe4, Decl(genericFunctionInference1.ts, 48, 30)) +>box : Symbol(box, Decl(genericFunctionInference1.ts, 4, 36)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +declare function pipe5(f: (a: A) => B): { f: (a: A) => B }; +>pipe5 : Symbol(pipe5, Decl(genericFunctionInference1.ts, 53, 31)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 55, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 55, 25)) +>f : Symbol(f, Decl(genericFunctionInference1.ts, 55, 29)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 55, 33)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 55, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 55, 25)) +>f : Symbol(f, Decl(genericFunctionInference1.ts, 55, 47)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 55, 52)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 55, 23)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 55, 25)) + +const f50 = pipe5(list); // No higher order inference +>f50 : Symbol(f50, Decl(genericFunctionInference1.ts, 57, 5)) +>pipe5 : Symbol(pipe5, Decl(genericFunctionInference1.ts, 53, 31)) +>list : Symbol(list, Decl(genericFunctionInference1.ts, 2, 124)) + +// #417 + +function mirror(f: (a: A) => B): (a: A) => B { return f; } +>mirror : Symbol(mirror, Decl(genericFunctionInference1.ts, 57, 24)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 61, 16)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 61, 18)) +>f : Symbol(f, Decl(genericFunctionInference1.ts, 61, 22)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 61, 26)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 61, 16)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 61, 18)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 61, 40)) +>A : Symbol(A, Decl(genericFunctionInference1.ts, 61, 16)) +>B : Symbol(B, Decl(genericFunctionInference1.ts, 61, 18)) +>f : Symbol(f, Decl(genericFunctionInference1.ts, 61, 22)) + +var identityM = mirror(identity); +>identityM : Symbol(identityM, Decl(genericFunctionInference1.ts, 62, 3)) +>mirror : Symbol(mirror, Decl(genericFunctionInference1.ts, 57, 24)) +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 154, 13)) + +var x = 1; +>x : Symbol(x, Decl(genericFunctionInference1.ts, 64, 3)) + +var y = identity(x); +>y : Symbol(y, Decl(genericFunctionInference1.ts, 65, 3)) +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 154, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 64, 3)) + +var z = identityM(x); +>z : Symbol(z, Decl(genericFunctionInference1.ts, 66, 3)) +>identityM : Symbol(identityM, Decl(genericFunctionInference1.ts, 62, 3)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 64, 3)) + +// #3038 + +export function keyOf(value: { key: a; }): a { +>keyOf : Symbol(keyOf, Decl(genericFunctionInference1.ts, 66, 21)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 70, 22)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 70, 25)) +>key : Symbol(key, Decl(genericFunctionInference1.ts, 70, 33)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 70, 22)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 70, 22)) + + return value.key; +>value.key : Symbol(key, Decl(genericFunctionInference1.ts, 70, 33)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 70, 25)) +>key : Symbol(key, Decl(genericFunctionInference1.ts, 70, 33)) +} +export interface Data { +>Data : Symbol(Data, Decl(genericFunctionInference1.ts, 72, 1)) + + key: number; +>key : Symbol(Data.key, Decl(genericFunctionInference1.ts, 73, 23)) + + value: Date; +>value : Symbol(Data.value, Decl(genericFunctionInference1.ts, 74, 16)) +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +} + +var data: Data[] = []; +>data : Symbol(data, Decl(genericFunctionInference1.ts, 78, 3)) +>Data : Symbol(Data, Decl(genericFunctionInference1.ts, 72, 1)) + +declare function toKeys(values: a[], toKey: (value: a) => string): string[]; +>toKeys : Symbol(toKeys, Decl(genericFunctionInference1.ts, 78, 22)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 80, 24)) +>values : Symbol(values, Decl(genericFunctionInference1.ts, 80, 27)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 80, 24)) +>toKey : Symbol(toKey, Decl(genericFunctionInference1.ts, 80, 39)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 80, 48)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 80, 24)) + +toKeys(data, keyOf); // Error +>toKeys : Symbol(toKeys, Decl(genericFunctionInference1.ts, 78, 22)) +>data : Symbol(data, Decl(genericFunctionInference1.ts, 78, 3)) +>keyOf : Symbol(keyOf, Decl(genericFunctionInference1.ts, 66, 21)) + +// #9366 + +function flip(f: (a: a, b: b) => c): (b: b, a: a) => c { +>flip : Symbol(flip, Decl(genericFunctionInference1.ts, 82, 20)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 86, 14)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 86, 16)) +>c : Symbol(c, Decl(genericFunctionInference1.ts, 86, 19)) +>f : Symbol(f, Decl(genericFunctionInference1.ts, 86, 23)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 86, 27)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 86, 14)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 86, 32)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 86, 16)) +>c : Symbol(c, Decl(genericFunctionInference1.ts, 86, 19)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 86, 47)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 86, 16)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 86, 52)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 86, 14)) +>c : Symbol(c, Decl(genericFunctionInference1.ts, 86, 19)) + + return (b: b, a: a) => f(a, b); +>b : Symbol(b, Decl(genericFunctionInference1.ts, 87, 10)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 86, 16)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 87, 15)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 86, 14)) +>f : Symbol(f, Decl(genericFunctionInference1.ts, 86, 23)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 87, 15)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 87, 10)) +} +function zip(x: T, y: U): [T, U] { +>zip : Symbol(zip, Decl(genericFunctionInference1.ts, 88, 1)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 89, 13)) +>U : Symbol(U, Decl(genericFunctionInference1.ts, 89, 15)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 89, 19)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 89, 13)) +>y : Symbol(y, Decl(genericFunctionInference1.ts, 89, 24)) +>U : Symbol(U, Decl(genericFunctionInference1.ts, 89, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 89, 13)) +>U : Symbol(U, Decl(genericFunctionInference1.ts, 89, 15)) + + return [x, y]; +>x : Symbol(x, Decl(genericFunctionInference1.ts, 89, 19)) +>y : Symbol(y, Decl(genericFunctionInference1.ts, 89, 24)) +} + +var expected: (y: U, x: T) => [T, U] = flip(zip); +>expected : Symbol(expected, Decl(genericFunctionInference1.ts, 93, 3)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 93, 15)) +>U : Symbol(U, Decl(genericFunctionInference1.ts, 93, 17)) +>y : Symbol(y, Decl(genericFunctionInference1.ts, 93, 21)) +>U : Symbol(U, Decl(genericFunctionInference1.ts, 93, 17)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 93, 26)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 93, 15)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 93, 15)) +>U : Symbol(U, Decl(genericFunctionInference1.ts, 93, 17)) +>flip : Symbol(flip, Decl(genericFunctionInference1.ts, 82, 20)) +>zip : Symbol(zip, Decl(genericFunctionInference1.ts, 88, 1)) + +var actual = flip(zip); +>actual : Symbol(actual, Decl(genericFunctionInference1.ts, 94, 3)) +>flip : Symbol(flip, Decl(genericFunctionInference1.ts, 82, 20)) +>zip : Symbol(zip, Decl(genericFunctionInference1.ts, 88, 1)) + +// #9366 + +const map = (transform: (t: T) => U) => +>map : Symbol(map, Decl(genericFunctionInference1.ts, 98, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 98, 13)) +>U : Symbol(U, Decl(genericFunctionInference1.ts, 98, 15)) +>transform : Symbol(transform, Decl(genericFunctionInference1.ts, 98, 19)) +>t : Symbol(t, Decl(genericFunctionInference1.ts, 98, 31)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 98, 13)) +>U : Symbol(U, Decl(genericFunctionInference1.ts, 98, 15)) + + (arr: T[]) => arr.map(transform) +>arr : Symbol(arr, Decl(genericFunctionInference1.ts, 99, 5)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 98, 13)) +>arr.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>arr : Symbol(arr, Decl(genericFunctionInference1.ts, 99, 5)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>transform : Symbol(transform, Decl(genericFunctionInference1.ts, 98, 19)) + +const identityStr = (t: string) => t; +>identityStr : Symbol(identityStr, Decl(genericFunctionInference1.ts, 101, 5)) +>t : Symbol(t, Decl(genericFunctionInference1.ts, 101, 21)) +>t : Symbol(t, Decl(genericFunctionInference1.ts, 101, 21)) + +const arr: string[] = map(identityStr)(['a']); +>arr : Symbol(arr, Decl(genericFunctionInference1.ts, 103, 5)) +>map : Symbol(map, Decl(genericFunctionInference1.ts, 98, 5)) +>identityStr : Symbol(identityStr, Decl(genericFunctionInference1.ts, 101, 5)) + +const arr1: string[] = map(identity)(['a']); +>arr1 : Symbol(arr1, Decl(genericFunctionInference1.ts, 104, 5)) +>map : Symbol(map, Decl(genericFunctionInference1.ts, 98, 5)) +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 154, 13)) + +// #9949 + +function of2(one: a, two: b): [a, b] { +>of2 : Symbol(of2, Decl(genericFunctionInference1.ts, 104, 44)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 108, 13)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 108, 15)) +>one : Symbol(one, Decl(genericFunctionInference1.ts, 108, 19)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 108, 13)) +>two : Symbol(two, Decl(genericFunctionInference1.ts, 108, 26)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 108, 15)) +>a : Symbol(a, Decl(genericFunctionInference1.ts, 108, 13)) +>b : Symbol(b, Decl(genericFunctionInference1.ts, 108, 15)) + + return [one, two]; +>one : Symbol(one, Decl(genericFunctionInference1.ts, 108, 19)) +>two : Symbol(two, Decl(genericFunctionInference1.ts, 108, 26)) +} + +const flipped = flip(of2); +>flipped : Symbol(flipped, Decl(genericFunctionInference1.ts, 112, 5)) +>flip : Symbol(flip, Decl(genericFunctionInference1.ts, 82, 20)) +>of2 : Symbol(of2, Decl(genericFunctionInference1.ts, 104, 44)) + +// #29904.1 + +type Component

= (props: P) => {}; +>Component : Symbol(Component, Decl(genericFunctionInference1.ts, 112, 26)) +>P : Symbol(P, Decl(genericFunctionInference1.ts, 116, 15)) +>props : Symbol(props, Decl(genericFunctionInference1.ts, 116, 21)) +>P : Symbol(P, Decl(genericFunctionInference1.ts, 116, 15)) + +declare const myHoc1:

(C: Component

) => Component

; +>myHoc1 : Symbol(myHoc1, Decl(genericFunctionInference1.ts, 118, 13)) +>P : Symbol(P, Decl(genericFunctionInference1.ts, 118, 23)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 118, 26)) +>Component : Symbol(Component, Decl(genericFunctionInference1.ts, 112, 26)) +>P : Symbol(P, Decl(genericFunctionInference1.ts, 118, 23)) +>Component : Symbol(Component, Decl(genericFunctionInference1.ts, 112, 26)) +>P : Symbol(P, Decl(genericFunctionInference1.ts, 118, 23)) + +declare const myHoc2:

(C: Component

) => Component

; +>myHoc2 : Symbol(myHoc2, Decl(genericFunctionInference1.ts, 119, 13)) +>P : Symbol(P, Decl(genericFunctionInference1.ts, 119, 23)) +>C : Symbol(C, Decl(genericFunctionInference1.ts, 119, 26)) +>Component : Symbol(Component, Decl(genericFunctionInference1.ts, 112, 26)) +>P : Symbol(P, Decl(genericFunctionInference1.ts, 119, 23)) +>Component : Symbol(Component, Decl(genericFunctionInference1.ts, 112, 26)) +>P : Symbol(P, Decl(genericFunctionInference1.ts, 119, 23)) + +declare const MyComponent1: Component<{ foo: 1 }>; +>MyComponent1 : Symbol(MyComponent1, Decl(genericFunctionInference1.ts, 121, 13)) +>Component : Symbol(Component, Decl(genericFunctionInference1.ts, 112, 26)) +>foo : Symbol(foo, Decl(genericFunctionInference1.ts, 121, 39)) + +const enhance = pipe( +>enhance : Symbol(enhance, Decl(genericFunctionInference1.ts, 123, 5)) +>pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) + + myHoc1, +>myHoc1 : Symbol(myHoc1, Decl(genericFunctionInference1.ts, 118, 13)) + + myHoc2, +>myHoc2 : Symbol(myHoc2, Decl(genericFunctionInference1.ts, 119, 13)) + +); + +const MyComponent2 = enhance(MyComponent1); +>MyComponent2 : Symbol(MyComponent2, Decl(genericFunctionInference1.ts, 128, 5)) +>enhance : Symbol(enhance, Decl(genericFunctionInference1.ts, 123, 5)) +>MyComponent1 : Symbol(MyComponent1, Decl(genericFunctionInference1.ts, 121, 13)) // #29904.2 const fn20 = pipe((_a?: {}) => 1); ->fn20 : Symbol(fn20, Decl(genericFunctionInference1.ts, 27, 5)) +>fn20 : Symbol(fn20, Decl(genericFunctionInference1.ts, 132, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) ->_a : Symbol(_a, Decl(genericFunctionInference1.ts, 27, 19)) +>_a : Symbol(_a, Decl(genericFunctionInference1.ts, 132, 19)) // #29904.3 type Fn = (n: number) => number; ->Fn : Symbol(Fn, Decl(genericFunctionInference1.ts, 27, 34)) ->n : Symbol(n, Decl(genericFunctionInference1.ts, 31, 11)) +>Fn : Symbol(Fn, Decl(genericFunctionInference1.ts, 132, 34)) +>n : Symbol(n, Decl(genericFunctionInference1.ts, 136, 11)) const fn30: Fn = pipe( ->fn30 : Symbol(fn30, Decl(genericFunctionInference1.ts, 32, 5)) ->Fn : Symbol(Fn, Decl(genericFunctionInference1.ts, 27, 34)) +>fn30 : Symbol(fn30, Decl(genericFunctionInference1.ts, 137, 5)) +>Fn : Symbol(Fn, Decl(genericFunctionInference1.ts, 132, 34)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) x => x + 1, ->x : Symbol(x, Decl(genericFunctionInference1.ts, 32, 22)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 32, 22)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 137, 22)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 137, 22)) x => x * 2, ->x : Symbol(x, Decl(genericFunctionInference1.ts, 33, 15)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 33, 15)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 138, 15)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 138, 15)) ); const promise = Promise.resolve(1); ->promise : Symbol(promise, Decl(genericFunctionInference1.ts, 37, 5)) +>promise : Symbol(promise, Decl(genericFunctionInference1.ts, 142, 5)) >Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) >Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) promise.then( >promise.then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) ->promise : Symbol(promise, Decl(genericFunctionInference1.ts, 37, 5)) +>promise : Symbol(promise, Decl(genericFunctionInference1.ts, 142, 5)) >then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) pipe( >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) x => x + 1, ->x : Symbol(x, Decl(genericFunctionInference1.ts, 39, 9)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 39, 9)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 144, 9)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 144, 9)) x => x * 2, ->x : Symbol(x, Decl(genericFunctionInference1.ts, 40, 19)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 40, 19)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 145, 19)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 145, 19)) ), ); @@ -288,94 +757,94 @@ promise.then( // #29904.4 declare const getString: () => string; ->getString : Symbol(getString, Decl(genericFunctionInference1.ts, 47, 13)) +>getString : Symbol(getString, Decl(genericFunctionInference1.ts, 152, 13)) declare const orUndefined: (name: string) => string | undefined; ->orUndefined : Symbol(orUndefined, Decl(genericFunctionInference1.ts, 48, 13)) ->name : Symbol(name, Decl(genericFunctionInference1.ts, 48, 28)) +>orUndefined : Symbol(orUndefined, Decl(genericFunctionInference1.ts, 153, 13)) +>name : Symbol(name, Decl(genericFunctionInference1.ts, 153, 28)) declare const identity: (value: T) => T; ->identity : Symbol(identity, Decl(genericFunctionInference1.ts, 49, 13)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 49, 25)) ->value : Symbol(value, Decl(genericFunctionInference1.ts, 49, 28)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 49, 25)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 49, 25)) +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 154, 13)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 154, 25)) +>value : Symbol(value, Decl(genericFunctionInference1.ts, 154, 28)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 154, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 154, 25)) const fn40 = pipe( ->fn40 : Symbol(fn40, Decl(genericFunctionInference1.ts, 51, 5)) +>fn40 : Symbol(fn40, Decl(genericFunctionInference1.ts, 156, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) getString, ->getString : Symbol(getString, Decl(genericFunctionInference1.ts, 47, 13)) +>getString : Symbol(getString, Decl(genericFunctionInference1.ts, 152, 13)) string => orUndefined(string), ->string : Symbol(string, Decl(genericFunctionInference1.ts, 52, 14)) ->orUndefined : Symbol(orUndefined, Decl(genericFunctionInference1.ts, 48, 13)) ->string : Symbol(string, Decl(genericFunctionInference1.ts, 52, 14)) +>string : Symbol(string, Decl(genericFunctionInference1.ts, 157, 14)) +>orUndefined : Symbol(orUndefined, Decl(genericFunctionInference1.ts, 153, 13)) +>string : Symbol(string, Decl(genericFunctionInference1.ts, 157, 14)) identity, ->identity : Symbol(identity, Decl(genericFunctionInference1.ts, 49, 13)) +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 154, 13)) ); // #29904.6 declare const getArray: () => string[]; ->getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 59, 13)) +>getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 164, 13)) declare const first: (ts: T[]) => T; ->first : Symbol(first, Decl(genericFunctionInference1.ts, 60, 13)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 60, 22)) ->ts : Symbol(ts, Decl(genericFunctionInference1.ts, 60, 25)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 60, 22)) ->T : Symbol(T, Decl(genericFunctionInference1.ts, 60, 22)) +>first : Symbol(first, Decl(genericFunctionInference1.ts, 165, 13)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 165, 22)) +>ts : Symbol(ts, Decl(genericFunctionInference1.ts, 165, 25)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 165, 22)) +>T : Symbol(T, Decl(genericFunctionInference1.ts, 165, 22)) const fn60 = pipe( ->fn60 : Symbol(fn60, Decl(genericFunctionInference1.ts, 62, 5)) +>fn60 : Symbol(fn60, Decl(genericFunctionInference1.ts, 167, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) getArray, ->getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 59, 13)) +>getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 164, 13)) x => x, ->x : Symbol(x, Decl(genericFunctionInference1.ts, 63, 13)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 63, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 168, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 168, 13)) first, ->first : Symbol(first, Decl(genericFunctionInference1.ts, 60, 13)) +>first : Symbol(first, Decl(genericFunctionInference1.ts, 165, 13)) ); const fn61 = pipe( ->fn61 : Symbol(fn61, Decl(genericFunctionInference1.ts, 68, 5)) +>fn61 : Symbol(fn61, Decl(genericFunctionInference1.ts, 173, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) getArray, ->getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 59, 13)) +>getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 164, 13)) identity, ->identity : Symbol(identity, Decl(genericFunctionInference1.ts, 49, 13)) +>identity : Symbol(identity, Decl(genericFunctionInference1.ts, 154, 13)) first, ->first : Symbol(first, Decl(genericFunctionInference1.ts, 60, 13)) +>first : Symbol(first, Decl(genericFunctionInference1.ts, 165, 13)) ); const fn62 = pipe( ->fn62 : Symbol(fn62, Decl(genericFunctionInference1.ts, 74, 5)) +>fn62 : Symbol(fn62, Decl(genericFunctionInference1.ts, 179, 5)) >pipe : Symbol(pipe, Decl(genericFunctionInference1.ts, 0, 0), Decl(genericFunctionInference1.ts, 0, 84), Decl(genericFunctionInference1.ts, 1, 104)) getArray, ->getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 59, 13)) +>getArray : Symbol(getArray, Decl(genericFunctionInference1.ts, 164, 13)) x => x, ->x : Symbol(x, Decl(genericFunctionInference1.ts, 75, 13)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 75, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 180, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 180, 13)) x => first(x), ->x : Symbol(x, Decl(genericFunctionInference1.ts, 76, 11)) ->first : Symbol(first, Decl(genericFunctionInference1.ts, 60, 13)) ->x : Symbol(x, Decl(genericFunctionInference1.ts, 76, 11)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 181, 11)) +>first : Symbol(first, Decl(genericFunctionInference1.ts, 165, 13)) +>x : Symbol(x, Decl(genericFunctionInference1.ts, 181, 11)) ); diff --git a/tests/baselines/reference/genericFunctionInference1.types b/tests/baselines/reference/genericFunctionInference1.types index cf85c1b5afb..e68b0ed6da0 100644 --- a/tests/baselines/reference/genericFunctionInference1.types +++ b/tests/baselines/reference/genericFunctionInference1.types @@ -32,6 +32,11 @@ declare function box(x: V): { value: V }; >x : V >value : V +declare function foo(x: T): T; +>foo : (x: T) => T +>value : T +>x : T + const f00 = pipe(list); >f00 : (a: T) => T[] >pipe(list) : (a: T) => T[] @@ -45,8 +50,15 @@ const f01 = pipe(list, box); >list : (a: T) => T[] >box : (x: V) => { value: V; } -const f02 = pipe(x => list(x), box); ->f02 : (x: any) => { value: any[]; } +const f02 = pipe(box, list); +>f02 : (x: V) => { value: V; }[] +>pipe(box, list) : (x: V) => { value: V; }[] +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>box : (x: V) => { value: V; } +>list : (a: T) => T[] + +const f03 = pipe(x => list(x), box); +>f03 : (x: any) => { value: any[]; } >pipe(x => list(x), box) : (x: any) => { value: any[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >x => list(x) : (x: any) => any[] @@ -56,8 +68,8 @@ const f02 = pipe(x => list(x), box); >x : any >box : (x: V) => { value: V; } -const f03 = pipe(list, x => box(x)); ->f03 : (a: T) => { value: T[]; } +const f04 = pipe(list, x => box(x)); +>f04 : (a: T) => { value: T[]; } >pipe(list, x => box(x)) : (a: T) => { value: T[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >list : (a: T) => T[] @@ -67,8 +79,8 @@ const f03 = pipe(list, x => box(x)); >box : (x: V) => { value: V; } >x : T[] -const f04 = pipe(x => list(x), x => box(x)) ->f04 : (x: any) => { value: any[]; } +const f05 = pipe(x => list(x), x => box(x)) +>f05 : (x: any) => { value: any[]; } >pipe(x => list(x), x => box(x)) : (x: any) => { value: any[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >x => list(x) : (x: any) => any[] @@ -82,8 +94,8 @@ const f04 = pipe(x => list(x), x => box(x)) >box : (x: V) => { value: V; } >x : any[] -const f05 = pipe(list, pipe(box)); ->f05 : (a: T) => { value: T[]; } +const f06 = pipe(list, pipe(box)); +>f06 : (a: T) => { value: T[]; } >pipe(list, pipe(box)) : (a: T) => { value: T[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >list : (a: T) => T[] @@ -91,8 +103,8 @@ const f05 = pipe(list, pipe(box)); >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >box : (x: V) => { value: V; } -const f06 = pipe(x => list(x), pipe(box)); ->f06 : (x: any) => { value: any[]; } +const f07 = pipe(x => list(x), pipe(box)); +>f07 : (x: any) => { value: any[]; } >pipe(x => list(x), pipe(box)) : (x: any) => { value: any[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >x => list(x) : (x: any) => any[] @@ -104,8 +116,8 @@ const f06 = pipe(x => list(x), pipe(box)); >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >box : (x: V) => { value: V; } -const f07 = pipe(x => list(x), pipe(x => box(x))); ->f07 : (x: any) => { value: any[]; } +const f08 = pipe(x => list(x), pipe(x => box(x))); +>f08 : (x: any) => { value: any[]; } >pipe(x => list(x), pipe(x => box(x))) : (x: any) => { value: any[]; } >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >x => list(x) : (x: any) => any[] @@ -121,15 +133,39 @@ const f07 = pipe(x => list(x), pipe(x => box(x))); >box : (x: V) => { value: V; } >x : any[] -const f10: (x: T) => T[] = pipe(list); ->f10 : (x: T) => T[] +const f09 = pipe(list, x => x.length); +>f09 : (a: T) => number +>pipe(list, x => x.length) : (a: T) => number +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] +>x => x.length : (x: T[]) => number +>x : T[] +>x.length : number +>x : T[] +>length : number + +const f10 = pipe(foo); +>f10 : (x: T) => T +>pipe(foo) : (x: T) => T +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>foo : (x: T) => T + +const f11 = pipe(foo, foo); +>f11 : (x: T) => T +>pipe(foo, foo) : (x: T) => T +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>foo : (x: T) => T +>foo : (x: T) => T + +const g00: (x: T) => T[] = pipe(list); +>g00 : (x: T) => T[] >x : T >pipe(list) : (a: T) => T[] >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >list : (a: T) => T[] -const f11: (x: T) => { value: T[] } = pipe(list, box); ->f11 : (x: T) => { value: T[]; } +const g01: (x: T) => { value: T[] } = pipe(list, box); +>g01 : (x: T) => { value: T[]; } >x : T >value : T[] >pipe(list, box) : (a: T) => { value: T[]; } @@ -137,8 +173,17 @@ const f11: (x: T) => { value: T[] } = pipe(list, box); >list : (a: T) => T[] >box : (x: V) => { value: V; } -const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); ->f12 : (x: T) => { value: T[]; } +const g02: (x: T) => { value: T }[] = pipe(box, list); +>g02 : (x: T) => { value: T; }[] +>x : T +>value : T +>pipe(box, list) : (x: T) => { value: T; }[] +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>box : (x: V) => { value: V; } +>list : (a: T) => T[] + +const g03: (x: T) => { value: T[] } = pipe(x => list(x), box); +>g03 : (x: T) => { value: T[]; } >x : T >value : T[] >pipe(x => list(x), box) : (x: T) => { value: T[]; } @@ -150,8 +195,8 @@ const f12: (x: T) => { value: T[] } = pipe(x => list(x), box); >x : T >box : (x: V) => { value: V; } -const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); ->f13 : (x: T) => { value: T[]; } +const g04: (x: T) => { value: T[] } = pipe(list, x => box(x)); +>g04 : (x: T) => { value: T[]; } >x : T >value : T[] >pipe(list, x => box(x)) : (a: T) => { value: T[]; } @@ -163,8 +208,8 @@ const f13: (x: T) => { value: T[] } = pipe(list, x => box(x)); >box : (x: V) => { value: V; } >x : T[] -const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) ->f14 : (x: T) => { value: T[]; } +const g05: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) +>g05 : (x: T) => { value: T[]; } >x : T >value : T[] >pipe(x => list(x), x => box(x)) : (x: T) => { value: T[]; } @@ -180,8 +225,8 @@ const f14: (x: T) => { value: T[] } = pipe(x => list(x), x => box(x)) >box : (x: V) => { value: V; } >x : T[] -const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); ->f15 : (x: T) => { value: T[]; } +const g06: (x: T) => { value: T[] } = pipe(list, pipe(box)); +>g06 : (x: T) => { value: T[]; } >x : T >value : T[] >pipe(list, pipe(box)) : (a: T) => { value: T[]; } @@ -191,8 +236,8 @@ const f15: (x: T) => { value: T[] } = pipe(list, pipe(box)); >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >box : (x: V) => { value: V; } -const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); ->f16 : (x: T) => { value: T[]; } +const g07: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); +>g07 : (x: T) => { value: T[]; } >x : T >value : T[] >pipe(x => list(x), pipe(box)) : (x: T) => { value: T[]; } @@ -206,8 +251,8 @@ const f16: (x: T) => { value: T[] } = pipe(x => list(x), pipe(box)); >pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } >box : (x: V) => { value: V; } -const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); ->f17 : (x: T) => { value: T[]; } +const g08: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); +>g08 : (x: T) => { value: T[]; } >x : T >value : T[] >pipe(x => list(x), pipe(x => box(x))) : (x: T) => { value: T[]; } @@ -225,6 +270,365 @@ const f17: (x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x))); >box : (x: V) => { value: V; } >x : T[] +const g09: (x: T) => number = pipe(list, x => x.length); +>g09 : (x: T) => number +>x : T +>pipe(list, x => x.length) : (a: T) => number +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>list : (a: T) => T[] +>x => x.length : (x: T[]) => number +>x : T[] +>x.length : number +>x : T[] +>length : number + +const g10: (x: T) => T = pipe(foo); +>g10 : (x: T) => T +>value : T +>x : T +>pipe(foo) : (x: T) => T +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>foo : (x: T) => T + +const g12: (x: T) => T = pipe(foo, foo); +>g12 : (x: T) => T +>value : T +>x : T +>pipe(foo, foo) : (x: T) => T +>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } +>foo : (x: T) => T +>foo : (x: T) => T + +declare function pipe2(ab: (a: A) => B, cd: (c: C) => D): (a: [A, C]) => [B, D]; +>pipe2 : (ab: (a: A) => B, cd: (c: C) => D) => (a: [A, C]) => [B, D] +>ab : (a: A) => B +>a : A +>cd : (c: C) => D +>c : C +>a : [A, C] + +const f20 = pipe2(list, box); +>f20 : (a: [T, V]) => [T[], { value: V; }] +>pipe2(list, box) : (a: [T, V]) => [T[], { value: V; }] +>pipe2 : (ab: (a: A) => B, cd: (c: C) => D) => (a: [A, C]) => [B, D] +>list : (a: T) => T[] +>box : (x: V) => { value: V; } + +const f21 = pipe2(box, list); +>f21 : (a: [V, T]) => [{ value: V; }, T[]] +>pipe2(box, list) : (a: [V, T]) => [{ value: V; }, T[]] +>pipe2 : (ab: (a: A) => B, cd: (c: C) => D) => (a: [A, C]) => [B, D] +>box : (x: V) => { value: V; } +>list : (a: T) => T[] + +const f22 = pipe2(list, list); +>f22 : (a: [T, T1]) => [T[], T1[]] +>pipe2(list, list) : (a: [T, T1]) => [T[], T1[]] +>pipe2 : (ab: (a: A) => B, cd: (c: C) => D) => (a: [A, C]) => [B, D] +>list : (a: T) => T[] +>list : (a: T) => T[] + +const f23 = pipe2(box, box); +>f23 : (a: [V, V1]) => [{ value: V; }, { value: V1; }] +>pipe2(box, box) : (a: [V, V1]) => [{ value: V; }, { value: V1; }] +>pipe2 : (ab: (a: A) => B, cd: (c: C) => D) => (a: [A, C]) => [B, D] +>box : (x: V) => { value: V; } +>box : (x: V) => { value: V; } + +const f24 = pipe2(f20, f20); +>f24 : (a: [[T, V], [T1, V1]]) => [[T[], { value: V; }], [T1[], { value: V1; }]] +>pipe2(f20, f20) : (a: [[T, V], [T1, V1]]) => [[T[], { value: V; }], [T1[], { value: V1; }]] +>pipe2 : (ab: (a: A) => B, cd: (c: C) => D) => (a: [A, C]) => [B, D] +>f20 : (a: [T, V]) => [T[], { value: V; }] +>f20 : (a: [T, V]) => [T[], { value: V; }] + +const f25 = pipe2(foo, foo); +>f25 : (a: [T, T1]) => [T, T1] +>pipe2(foo, foo) : (a: [T, T1]) => [T, T1] +>pipe2 : (ab: (a: A) => B, cd: (c: C) => D) => (a: [A, C]) => [B, D] +>foo : (x: T) => T +>foo : (x: T) => T + +const f26 = pipe2(f25, f25); +>f26 : (a: [[T, T1], [T2, T3]]) => [[T, T1], [T2, T3]] +>pipe2(f25, f25) : (a: [[T, T1], [T2, T3]]) => [[T, T1], [T2, T3]] +>pipe2 : (ab: (a: A) => B, cd: (c: C) => D) => (a: [A, C]) => [B, D] +>f25 : (a: [T, T1]) => [T, T1] +>f25 : (a: [T, T1]) => [T, T1] + +declare function pipe3(ab: (a: A) => B, ac: (a: A) => C): (a: A) => [B, C]; +>pipe3 : (ab: (a: A) => B, ac: (a: A) => C) => (a: A) => [B, C] +>ab : (a: A) => B +>a : A +>ac : (a: A) => C +>a : A +>a : A + +const f30 = pipe3(list, box); +>f30 : (a: T) => [T[], { value: T; }] +>pipe3(list, box) : (a: T) => [T[], { value: T; }] +>pipe3 : (ab: (a: A) => B, ac: (a: A) => C) => (a: A) => [B, C] +>list : (a: T) => T[] +>box : (x: V) => { value: V; } + +const f31 = pipe3(box, list); +>f31 : (a: V) => [{ value: V; }, V[]] +>pipe3(box, list) : (a: V) => [{ value: V; }, V[]] +>pipe3 : (ab: (a: A) => B, ac: (a: A) => C) => (a: A) => [B, C] +>box : (x: V) => { value: V; } +>list : (a: T) => T[] + +const f32 = pipe3(list, list); +>f32 : (a: T) => [T[], T[]] +>pipe3(list, list) : (a: T) => [T[], T[]] +>pipe3 : (ab: (a: A) => B, ac: (a: A) => C) => (a: A) => [B, C] +>list : (a: T) => T[] +>list : (a: T) => T[] + +declare function pipe4(funcs: [(a: A) => B, (b: B) => C]): (a: A) => C; +>pipe4 : (funcs: [(a: A) => B, (b: B) => C]) => (a: A) => C +>funcs : [(a: A) => B, (b: B) => C] +>a : A +>b : B +>a : A + +const f40 = pipe4([list, box]); +>f40 : (a: T) => { value: T[]; } +>pipe4([list, box]) : (a: T) => { value: T[]; } +>pipe4 : (funcs: [(a: A) => B, (b: B) => C]) => (a: A) => C +>[list, box] : [(a: T) => T[], (x: V) => { value: V; }] +>list : (a: T) => T[] +>box : (x: V) => { value: V; } + +const f41 = pipe4([box, list]); +>f41 : (a: V) => { value: V; }[] +>pipe4([box, list]) : (a: V) => { value: V; }[] +>pipe4 : (funcs: [(a: A) => B, (b: B) => C]) => (a: A) => C +>[box, list] : [(x: V) => { value: V; }, (a: T) => T[]] +>box : (x: V) => { value: V; } +>list : (a: T) => T[] + +declare function pipe5(f: (a: A) => B): { f: (a: A) => B }; +>pipe5 : (f: (a: A) => B) => { f: (a: A) => B; } +>f : (a: A) => B +>a : A +>f : (a: A) => B +>a : A + +const f50 = pipe5(list); // No higher order inference +>f50 : { f: (a: {}) => {}[]; } +>pipe5(list) : { f: (a: {}) => {}[]; } +>pipe5 : (f: (a: A) => B) => { f: (a: A) => B; } +>list : (a: T) => T[] + +// #417 + +function mirror(f: (a: A) => B): (a: A) => B { return f; } +>mirror : (f: (a: A) => B) => (a: A) => B +>f : (a: A) => B +>a : A +>a : A +>f : (a: A) => B + +var identityM = mirror(identity); +>identityM : (a: T) => T +>mirror(identity) : (a: T) => T +>mirror : (f: (a: A) => B) => (a: A) => B +>identity : (value: T) => T + +var x = 1; +>x : number +>1 : 1 + +var y = identity(x); +>y : number +>identity(x) : number +>identity : (value: T) => T +>x : number + +var z = identityM(x); +>z : number +>identityM(x) : number +>identityM : (a: T) => T +>x : number + +// #3038 + +export function keyOf(value: { key: a; }): a { +>keyOf : (value: { key: a; }) => a +>value : { key: a; } +>key : a + + return value.key; +>value.key : a +>value : { key: a; } +>key : a +} +export interface Data { + key: number; +>key : number + + value: Date; +>value : Date +} + +var data: Data[] = []; +>data : Data[] +>[] : never[] + +declare function toKeys(values: a[], toKey: (value: a) => string): string[]; +>toKeys : (values: a[], toKey: (value: a) => string) => string[] +>values : a[] +>toKey : (value: a) => string +>value : a + +toKeys(data, keyOf); // Error +>toKeys(data, keyOf) : any +>toKeys : (values: a[], toKey: (value: a) => string) => string[] +>data : Data[] +>keyOf : (value: { key: a; }) => a + +// #9366 + +function flip(f: (a: a, b: b) => c): (b: b, a: a) => c { +>flip : (f: (a: a, b: b) => c) => (b: b, a: a) => c +>f : (a: a, b: b) => c +>a : a +>b : b +>b : b +>a : a + + return (b: b, a: a) => f(a, b); +>(b: b, a: a) => f(a, b) : (b: b, a: a) => c +>b : b +>a : a +>f(a, b) : c +>f : (a: a, b: b) => c +>a : a +>b : b +} +function zip(x: T, y: U): [T, U] { +>zip : (x: T, y: U) => [T, U] +>x : T +>y : U + + return [x, y]; +>[x, y] : [T, U] +>x : T +>y : U +} + +var expected: (y: U, x: T) => [T, U] = flip(zip); +>expected : (y: U, x: T) => [T, U] +>y : U +>x : T +>flip(zip) : (b: U, a: T) => [T, U] +>flip : (f: (a: a, b: b) => c) => (b: b, a: a) => c +>zip : (x: T, y: U) => [T, U] + +var actual = flip(zip); +>actual : (b: U, a: T) => [T, U] +>flip(zip) : (b: U, a: T) => [T, U] +>flip : (f: (a: a, b: b) => c) => (b: b, a: a) => c +>zip : (x: T, y: U) => [T, U] + +// #9366 + +const map = (transform: (t: T) => U) => +>map : (transform: (t: T) => U) => (arr: T[]) => U[] +>(transform: (t: T) => U) => (arr: T[]) => arr.map(transform) : (transform: (t: T) => U) => (arr: T[]) => U[] +>transform : (t: T) => U +>t : T + + (arr: T[]) => arr.map(transform) +>(arr: T[]) => arr.map(transform) : (arr: T[]) => U[] +>arr : T[] +>arr.map(transform) : U[] +>arr.map : (callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[] +>arr : T[] +>map : (callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[] +>transform : (t: T) => U + +const identityStr = (t: string) => t; +>identityStr : (t: string) => string +>(t: string) => t : (t: string) => string +>t : string +>t : string + +const arr: string[] = map(identityStr)(['a']); +>arr : string[] +>map(identityStr)(['a']) : string[] +>map(identityStr) : (arr: string[]) => string[] +>map : (transform: (t: T) => U) => (arr: T[]) => U[] +>identityStr : (t: string) => string +>['a'] : string[] +>'a' : "a" + +const arr1: string[] = map(identity)(['a']); +>arr1 : string[] +>map(identity)(['a']) : string[] +>map(identity) : (arr: T[]) => T[] +>map : (transform: (t: T) => U) => (arr: T[]) => U[] +>identity : (value: T) => T +>['a'] : string[] +>'a' : "a" + +// #9949 + +function of2(one: a, two: b): [a, b] { +>of2 : (one: a, two: b) => [a, b] +>one : a +>two : b + + return [one, two]; +>[one, two] : [a, b] +>one : a +>two : b +} + +const flipped = flip(of2); +>flipped : (b: b, a: a) => [a, b] +>flip(of2) : (b: b, a: a) => [a, b] +>flip : (f: (a: a, b: b) => c) => (b: b, a: a) => c +>of2 : (one: a, two: b) => [a, b] + +// #29904.1 + +type Component

= (props: P) => {}; +>Component : Component

+>props : P + +declare const myHoc1:

(C: Component

) => Component

; +>myHoc1 :

(C: Component

) => Component

+>C : Component

+ +declare const myHoc2:

(C: Component

) => Component

; +>myHoc2 :

(C: Component

) => Component

+>C : Component

+ +declare const MyComponent1: Component<{ foo: 1 }>; +>MyComponent1 : Component<{ foo: 1; }> +>foo : 1 + +const enhance = pipe( +>enhance :

(C: Component

) => Component

+>pipe( myHoc1, myHoc2,) :

(C: Component

) => Component

+>pipe : { (ab: (...args: A) => B): (...args: A) => B; (ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; (ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; } + + myHoc1, +>myHoc1 :

(C: Component

) => Component

+ + myHoc2, +>myHoc2 :

(C: Component

) => Component

+ +); + +const MyComponent2 = enhance(MyComponent1); +>MyComponent2 : Component<{ foo: 1; }> +>enhance(MyComponent1) : Component<{ foo: 1; }> +>enhance :

(C: Component

) => Component

+>MyComponent1 : Component<{ foo: 1; }> + // #29904.2 const fn20 = pipe((_a?: {}) => 1); From bf326aaabf48e7b72ae68b4a10d33ee23cee97e8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 4 Mar 2019 11:15:09 -0800 Subject: [PATCH 68/83] Fix lint error --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9ca8b60d36a..4493b9e00a3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23451,7 +23451,7 @@ namespace ts { } function getUniqueTypeParameters(context: InferenceContext, typeParameters: ReadonlyArray): ReadonlyArray { - let result: TypeParameter[] = []; + const result: TypeParameter[] = []; let oldTypeParameters: TypeParameter[] | undefined; let newTypeParameters: TypeParameter[] | undefined; for (const tp of typeParameters) { From ad823daabba75c8d6d77ab5f9194ce5feaa1fa96 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 5 Mar 2019 06:09:54 -0800 Subject: [PATCH 69/83] Consistently defer generic functions to second type inference pass --- src/compiler/checker.ts | 27 +++++++++++++++++++-------- src/compiler/types.ts | 1 + 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4493b9e00a3..8cceb85a19a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20138,7 +20138,7 @@ namespace ts { const paramType = getTypeAtPosition(signature, i); // For context sensitive arguments we pass the identityMapper, which is a signal to treat all // context sensitive function expressions as wildcards - const checkMode = (excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | + const checkMode = (excludeArgument && i < excludeArgument.length && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | (excludeArgument ? CheckMode.SkipGenericFunctions : 0); const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); inferTypes(context.inferences, argType, paramType); @@ -20241,7 +20241,7 @@ namespace ts { // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, // can be specified by users through attributes property. const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); - const checkMode = excludeArgument && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; + const checkMode = excludeArgument && excludeArgument.length > 0 && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined, checkMode); return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes); } @@ -20276,13 +20276,13 @@ namespace ts { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { const paramType = getTypeAtPosition(signature, i); - const checkMode = (excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | + const checkMode = (excludeArgument && i < excludeArgument.length && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | (excludeArgument ? CheckMode.SkipGenericFunctions : 0); const argType = checkExpressionWithContextualType(arg, paramType, /*contextualMapper*/ undefined, checkMode); // If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter), // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). - const checkArgType = excludeArgument ? getRegularTypeOfObjectLiteral(argType) : argType; + const checkArgType = excludeArgument && excludeArgument.length ? getRegularTypeOfObjectLiteral(argType) : argType; if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage)) { return false; } @@ -20665,7 +20665,7 @@ namespace ts { } else { inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext); + typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument || emptyArray, inferenceContext); } checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); // If the original signature has a generic rest type, instantiation may produce a @@ -20678,14 +20678,14 @@ namespace ts { else { checkCandidate = candidate; } - if (!checkApplicableSignature(node, args, checkCandidate, relation, excludeArgument, /*reportErrors*/ false)) { + if (!checkApplicableSignature(node, args, checkCandidate, relation, excludeArgument || inferenceContext && emptyArray, /*reportErrors*/ false)) { // Give preference to error candidates that have no rest parameters (as they are more specific) if (!candidateForArgumentError || getEffectiveRestType(candidateForArgumentError) || !getEffectiveRestType(checkCandidate)) { candidateForArgumentError = checkCandidate; } continue; } - if (excludeArgument) { + if (excludeArgument || inferenceContext && inferenceContext.flags & InferenceFlags.SkippedGenericFunction) { // If one or more context sensitive arguments were excluded, we start including // them now (and keeping do so for any subsequent candidates) and perform a second // round of type inference and applicability checking for this particular candidate. @@ -20830,7 +20830,7 @@ namespace ts { function inferSignatureInstantiationForOverloadFailure(node: CallLikeExpression, typeParameters: ReadonlyArray, candidate: Signature, args: ReadonlyArray): Signature { const inferenceContext = createInferenceContext(typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args), inferenceContext); + const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args) || emptyArray, inferenceContext); return createSignatureInstantiation(candidate, typeArgumentTypes); } @@ -20932,6 +20932,7 @@ namespace ts { // sensitive arguments are being deferred) and every call signature is generic and returns a function type, // we return resolvingSignature here. This result will be propagated out and turned into anyFunctionType. if (checkMode & CheckMode.SkipGenericFunctions && callSignatures.every(isGenericFunctionReturningFunction)) { + skippedGenericFunction(node, checkMode); return resolvingSignature; } // If the function is explicitly marked with `@class`, then it must be constructed. @@ -23387,6 +23388,7 @@ namespace ts { const signature = getSingleCallSignature(type); if (signature && signature.typeParameters) { if (checkMode & CheckMode.SkipGenericFunctions) { + skippedGenericFunction(node, checkMode); return anyFunctionType; } const contextualType = getApparentTypeOfContextualType(node); @@ -23429,6 +23431,15 @@ namespace ts { return type; } + function skippedGenericFunction(node: Node, checkMode: CheckMode) { + if (checkMode & CheckMode.Inferential) { + // We have skipped a generic function during inferential typing. Obtain the inference context and + // indicate this has occurred such that we know a second pass of inference is be needed. + const context = getContextualMapper(node); + context.flags |= InferenceFlags.SkippedGenericFunction; + } + } + function hasInferenceCandidates(info: InferenceInfo) { return !!(info.candidates || info.contraCandidates); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5217dd66fb4..0d24db8454e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4362,6 +4362,7 @@ namespace ts { NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType) AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType) NoFixing = 1 << 2, // Disable type parameter fixing + SkippedGenericFunction = 1 << 3, } /** From cde94441d31543f414bbeb70a6700d30ee7fbc39 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 5 Mar 2019 06:10:21 -0800 Subject: [PATCH 70/83] Accept new baselines --- tests/baselines/reference/genericRestParameters1.js | 2 +- tests/baselines/reference/genericRestParameters1.types | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/genericRestParameters1.js b/tests/baselines/reference/genericRestParameters1.js index 3d8703213ac..2932f2b96cb 100644 --- a/tests/baselines/reference/genericRestParameters1.js +++ b/tests/baselines/reference/genericRestParameters1.js @@ -309,7 +309,7 @@ declare function f16(a: A, b: B): A | B; declare let x20: number; declare let x21: string; declare let x22: string | number; -declare let x23: unknown; +declare let x23: string | number; declare let x24: string | number; declare let x30: string; declare let x31: string | number; diff --git a/tests/baselines/reference/genericRestParameters1.types b/tests/baselines/reference/genericRestParameters1.types index 497561c8a98..c9067b38c97 100644 --- a/tests/baselines/reference/genericRestParameters1.types +++ b/tests/baselines/reference/genericRestParameters1.types @@ -462,8 +462,8 @@ let x22 = call(f15, "hello", 42); // string | number >42 : 42 let x23 = call(f16, "hello", 42); // unknown ->x23 : unknown ->call(f16, "hello", 42) : unknown +>x23 : string | number +>call(f16, "hello", 42) : string | number >call : (f: (...args: T) => U, ...args: T) => U >f16 : (a: A, b: B) => A | B >"hello" : "hello" From 6c790c02d9b7ed76f29989e8390d52d295c5d78c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 5 Mar 2019 12:42:54 -0800 Subject: [PATCH 71/83] Remove unnecessary excludeArgument array and getExcludeArgument function --- src/compiler/checker.ts | 66 ++++++++++++----------------------------- 1 file changed, 19 insertions(+), 47 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8cceb85a19a..ad6b87daa1a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20068,15 +20068,14 @@ namespace ts { return getSignatureInstantiation(signature, getInferredTypes(context), isInJSFile(contextualSignature.declaration)); } - function inferJsxTypeArguments(node: JsxOpeningLikeElement, signature: Signature, excludeArgument: ReadonlyArray | undefined, context: InferenceContext): Type[] { + function inferJsxTypeArguments(node: JsxOpeningLikeElement, signature: Signature, checkMode: CheckMode, context: InferenceContext): Type[] { const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); - const checkMode = excludeArgument && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; const checkAttrType = checkExpressionWithContextualType(node.attributes, paramType, context, checkMode); inferTypes(context.inferences, checkAttrType, paramType); return getInferredTypes(context); } - function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray, excludeArgument: ReadonlyArray | undefined, context: InferenceContext): Type[] { + function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray, checkMode: CheckMode, context: InferenceContext): Type[] { // Clear out all the inference results from the last time inferTypeArguments was called on this context for (const inference of context.inferences) { // As an optimization, we don't have to clear (and later recompute) inferred types @@ -20089,7 +20088,7 @@ namespace ts { } if (isJsxOpeningLikeElement(node)) { - return inferJsxTypeArguments(node, signature, excludeArgument, context); + return inferJsxTypeArguments(node, signature, checkMode, context); } // If a contextual type is available, infer from that type to the return type of the call expression. For @@ -20136,10 +20135,6 @@ namespace ts { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { const paramType = getTypeAtPosition(signature, i); - // For context sensitive arguments we pass the identityMapper, which is a signal to treat all - // context sensitive function expressions as wildcards - const checkMode = (excludeArgument && i < excludeArgument.length && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | - (excludeArgument ? CheckMode.SkipGenericFunctions : 0); const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); inferTypes(context.inferences, argType, paramType); } @@ -20234,14 +20229,12 @@ namespace ts { * @param node a JSX opening-like element we are trying to figure its call signature * @param signature a candidate signature we are trying whether it is a call signature * @param relation a relationship to check parameter and argument type - * @param excludeArgument */ - function checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, relation: Map, excludeArgument: boolean[] | undefined, reportErrors: boolean) { + function checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, relation: Map, checkMode: CheckMode, reportErrors: boolean) { // Stateless function components can have maximum of three arguments: "props", "context", and "updater". // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, // can be specified by users through attributes property. const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); - const checkMode = excludeArgument && excludeArgument.length > 0 && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined, checkMode); return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes); } @@ -20251,10 +20244,10 @@ namespace ts { args: ReadonlyArray, signature: Signature, relation: Map, - excludeArgument: boolean[] | undefined, + checkMode: CheckMode, reportErrors: boolean) { if (isJsxOpeningLikeElement(node)) { - return checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, excludeArgument, reportErrors); + return checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors); } const thisType = getThisTypeOfSignature(signature); if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) { @@ -20276,13 +20269,11 @@ namespace ts { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { const paramType = getTypeAtPosition(signature, i); - const checkMode = (excludeArgument && i < excludeArgument.length && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | - (excludeArgument ? CheckMode.SkipGenericFunctions : 0); const argType = checkExpressionWithContextualType(arg, paramType, /*contextualMapper*/ undefined, checkMode); - // If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter), + // If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive), // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). - const checkArgType = excludeArgument && excludeArgument.length ? getRegularTypeOfObjectLiteral(argType) : argType; + const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType; if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage)) { return false; } @@ -20540,7 +20531,7 @@ namespace ts { // For a decorator, no arguments are susceptible to contextual typing due to the fact // decorators are applied to a declaration by the emitter, and not to an expression. const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters; - let excludeArgument = !isDecorator && !isSingleNonGenericCandidate ? getExcludeArgument(args) : undefined; + let argCheckMode = !isDecorator && !isSingleNonGenericCandidate && some(args, isContextSensitive) ? CheckMode.SkipContextSensitive : CheckMode.Normal; // The following variables are captured and modified by calls to chooseOverload. // If overload resolution or type argument inference fails, we want to report the @@ -20599,12 +20590,7 @@ namespace ts { // skip the checkApplicableSignature check. if (reportErrors) { if (candidateForArgumentError) { - // excludeArgument is undefined, in this case also equivalent to [undefined, undefined, ...] - // The importance of excludeArgument is to prevent us from typing function expression parameters - // in arguments too early. If possible, we'd like to only type them once we know the correct - // overload. However, this matters for the case where the call is correct. When the call is - // an error, we don't need to exclude any arguments, although it would cause no harm to do so. - checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true); + checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, CheckMode.Normal, /*reportErrors*/ true); } else if (candidateForArgumentArityError) { diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args)); @@ -20638,7 +20624,7 @@ namespace ts { if (typeArguments || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { return undefined; } - if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) { + if (!checkApplicableSignature(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false)) { candidateForArgumentError = candidate; return undefined; } @@ -20665,7 +20651,8 @@ namespace ts { } else { inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument || emptyArray, inferenceContext); + typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext); + argCheckMode |= inferenceContext.flags & InferenceFlags.SkippedGenericFunction ? CheckMode.SkipGenericFunctions : CheckMode.Normal; } checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); // If the original signature has a generic rest type, instantiation may produce a @@ -20678,20 +20665,20 @@ namespace ts { else { checkCandidate = candidate; } - if (!checkApplicableSignature(node, args, checkCandidate, relation, excludeArgument || inferenceContext && emptyArray, /*reportErrors*/ false)) { + if (!checkApplicableSignature(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false)) { // Give preference to error candidates that have no rest parameters (as they are more specific) if (!candidateForArgumentError || getEffectiveRestType(candidateForArgumentError) || !getEffectiveRestType(checkCandidate)) { candidateForArgumentError = checkCandidate; } continue; } - if (excludeArgument || inferenceContext && inferenceContext.flags & InferenceFlags.SkippedGenericFunction) { + if (argCheckMode) { // If one or more context sensitive arguments were excluded, we start including // them now (and keeping do so for any subsequent candidates) and perform a second // round of type inference and applicability checking for this particular candidate. - excludeArgument = undefined; + argCheckMode = CheckMode.Normal; if (inferenceContext) { - const typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext); + const typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode, inferenceContext); checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); // If the original signature has a generic rest type, instantiation may produce a // signature with different arity and we need to perform another arity check. @@ -20700,7 +20687,7 @@ namespace ts { continue; } } - if (!checkApplicableSignature(node, args, checkCandidate, relation, excludeArgument, /*reportErrors*/ false)) { + if (!checkApplicableSignature(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false)) { // Give preference to error candidates that have no rest parameters (as they are more specific) if (!candidateForArgumentError || getEffectiveRestType(candidateForArgumentError) || !getEffectiveRestType(checkCandidate)) { candidateForArgumentError = checkCandidate; @@ -20716,21 +20703,6 @@ namespace ts { } } - function getExcludeArgument(args: ReadonlyArray): boolean[] | undefined { - let excludeArgument: boolean[] | undefined; - // We do not need to call `getEffectiveArgumentCount` here as it only - // applies when calculating the number of arguments for a decorator. - for (let i = 0; i < args.length; i++) { - if (isContextSensitive(args[i])) { - if (!excludeArgument) { - excludeArgument = new Array(args.length); - } - excludeArgument[i] = true; - } - } - return excludeArgument; - } - // No signature was applicable. We have already reported the errors for the invalid signature. // If this is a type resolution session, e.g. Language Service, try to get better information than anySignature. function getCandidateForOverloadFailure( @@ -20830,7 +20802,7 @@ namespace ts { function inferSignatureInstantiationForOverloadFailure(node: CallLikeExpression, typeParameters: ReadonlyArray, candidate: Signature, args: ReadonlyArray): Signature { const inferenceContext = createInferenceContext(typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args) || emptyArray, inferenceContext); + const typeArgumentTypes = inferTypeArguments(node, candidate, args, CheckMode.SkipContextSensitive | CheckMode.SkipGenericFunctions, inferenceContext); return createSignatureInstantiation(candidate, typeArgumentTypes); } From e55fbffed6e3a30331ab81ab5b84f486ea049c45 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Mar 2019 14:33:48 -0800 Subject: [PATCH 72/83] Rename the request as updateOpen --- src/server/protocol.ts | 12 ++++++------ src/server/session.ts | 2 +- .../unittests/tsserver/applyChangesToOpenFiles.ts | 6 +++--- tests/baselines/reference/api/tsserverlibrary.d.ts | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 3534a991c48..3b7690e2c60 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -92,7 +92,7 @@ namespace ts.server.protocol { SynchronizeProjectList = "synchronizeProjectList", /* @internal */ ApplyChangedToOpenFiles = "applyChangedToOpenFiles", - ApplyChangesToOpenFiles = "applyChangesToOpenFiles", + UpdateOpen = "updateOpen", /* @internal */ EncodedSemanticClassificationsFull = "encodedSemanticClassifications-full", /* @internal */ @@ -1547,15 +1547,15 @@ namespace ts.server.protocol { /** * Request to synchronize list of open files with the client */ - export interface ApplyChangesToOpenFilesRequest extends Request { - command: CommandTypes.ApplyChangesToOpenFiles; - arguments: ApplyChangesToOpenFilesRequestArgs; + export interface UpdateOpenRequest extends Request { + command: CommandTypes.UpdateOpen; + arguments: UpdateOpenRequestArgs; } /** - * Arguments to ApplyChangesToOpenFilesRequest + * Arguments to UpdateOpenRequest */ - export interface ApplyChangesToOpenFilesRequestArgs { + export interface UpdateOpenRequestArgs { /** * List of newly open files */ diff --git a/src/server/session.ts b/src/server/session.ts index af84ffff48a..7cc3f595f82 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -2096,7 +2096,7 @@ namespace ts.server { }); return this.requiredResponse(converted); }, - [CommandNames.ApplyChangesToOpenFiles]: (request: protocol.ApplyChangesToOpenFilesRequest) => { + [CommandNames.UpdateOpen]: (request: protocol.UpdateOpenRequest) => { this.changeSeq++; this.projectService.applyChangesInOpenFiles( request.arguments.openFiles && mapIterator(arrayIterator(request.arguments.openFiles), file => ({ diff --git a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts index 68c3839e789..316ecc8b652 100644 --- a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts +++ b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts @@ -105,10 +105,10 @@ ${file.content}`; ); }); - it("with applyChangesToOpenFiles request", () => { + it("with updateOpen request", () => { verify(session => - session.executeCommandSeq({ - command: protocol.CommandTypes.ApplyChangesToOpenFiles, + session.executeCommandSeq({ + command: protocol.CommandTypes.UpdateOpen, arguments: { openFiles: [ { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index f3fdd5158b4..5f4a400af8f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5720,7 +5720,7 @@ declare namespace ts.server.protocol { OpenExternalProject = "openExternalProject", OpenExternalProjects = "openExternalProjects", CloseExternalProject = "closeExternalProject", - ApplyChangesToOpenFiles = "applyChangesToOpenFiles", + UpdateOpen = "updateOpen", GetOutliningSpans = "getOutliningSpans", TodoComments = "todoComments", Indentation = "indentation", @@ -6792,14 +6792,14 @@ declare namespace ts.server.protocol { /** * Request to synchronize list of open files with the client */ - interface ApplyChangesToOpenFilesRequest extends Request { - command: CommandTypes.ApplyChangesToOpenFiles; - arguments: ApplyChangesToOpenFilesRequestArgs; + interface UpdateOpenRequest extends Request { + command: CommandTypes.UpdateOpen; + arguments: UpdateOpenRequestArgs; } /** - * Arguments to ApplyChangesToOpenFilesRequest + * Arguments to UpdateOpenRequest */ - interface ApplyChangesToOpenFilesRequestArgs { + interface UpdateOpenRequestArgs { /** * List of newly open files */ From d20132026e8a406fd8c891bc9b14e7246a83a31d Mon Sep 17 00:00:00 2001 From: Alexander T Date: Wed, 6 Mar 2019 01:06:30 +0200 Subject: [PATCH 73/83] remove sudo: false setting from travis.yml (#30231) --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b35dabcbb5f..2124f3ada33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,6 @@ node_js: - '10' - '8' -sudo: false - env: - workerCount=3 timeout=600000 From 60b2d6a2f5488c279f957bfca518b8591a15358a Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 5 Mar 2019 16:56:30 -0800 Subject: [PATCH 74/83] remove unnecessary closures --- .../refactors/convertToNamedParameters.ts | 216 +++++++++--------- 1 file changed, 107 insertions(+), 109 deletions(-) diff --git a/src/services/refactors/convertToNamedParameters.ts b/src/services/refactors/convertToNamedParameters.ts index b4220287bd0..61e9ccf9811 100644 --- a/src/services/refactors/convertToNamedParameters.ts +++ b/src/services/refactors/convertToNamedParameters.ts @@ -79,8 +79,7 @@ namespace ts.refactor.convertToNamedParameters { const checker = program.getTypeChecker(); const references = flatMap(names, /*mapfn*/ name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); - const isConstructor = isConstructorDeclaration(functionDeclaration); - const groupedReferences = groupReferences(references, isConstructor); + const groupedReferences = groupReferences(references); if (!every(groupedReferences.declarations, decl => contains(names, decl))) { groupedReferences.valid = false; @@ -88,11 +87,12 @@ namespace ts.refactor.convertToNamedParameters { return groupedReferences; - function groupReferences(referenceEntries: ReadonlyArray, isConstructor: boolean): GroupedReferences { + function groupReferences(referenceEntries: ReadonlyArray): GroupedReferences { const classReferences: ClassReferences = { accessExpressions: [], typeUsages: [] }; const groupedReferences: GroupedReferences = { functionCalls: [], declarations: [], classReferences, valid: true }; const functionSymbols = map(functionNames, checker.getSymbolAtLocation); const classSymbols = map(classNames, checker.getSymbolAtLocation); + const isConstructor = isConstructorDeclaration(functionDeclaration); for (const entry of referenceEntries) { if (entry.kind !== FindAllReferences.EntryKind.Node) { @@ -141,95 +141,93 @@ namespace ts.refactor.convertToNamedParameters { return groupedReferences; } + } - function symbolComparer(a: Symbol, b: Symbol): boolean { - return getSymbolTarget(a) === getSymbolTarget(b); + function symbolComparer(a: Symbol, b: Symbol): boolean { + return getSymbolTarget(a) === getSymbolTarget(b); + } + + function entryToDeclaration(entry: FindAllReferences.NodeEntry): Node | undefined { + if (isDeclaration(entry.node.parent)) { + return entry.node; } + return undefined; + } - function entryToDeclaration(entry: FindAllReferences.NodeEntry): Node | undefined { - if (isDeclaration(entry.node.parent)) { - return entry.node; - } - return undefined; - } - - function entryToFunctionCall(entry: FindAllReferences.NodeEntry): CallExpression | NewExpression | undefined { - if (entry.node.parent) { - const functionReference = entry.node; - const parent = functionReference.parent; - switch (parent.kind) { - // Function call (foo(...) or super(...)) - case SyntaxKind.CallExpression: - const callExpression = tryCast(parent, isCallExpression); - if (callExpression && callExpression.expression === functionReference) { + function entryToFunctionCall(entry: FindAllReferences.NodeEntry): CallExpression | NewExpression | undefined { + if (entry.node.parent) { + const functionReference = entry.node; + const parent = functionReference.parent; + switch (parent.kind) { + // Function call (foo(...) or super(...)) + case SyntaxKind.CallExpression: + const callExpression = tryCast(parent, isCallExpression); + if (callExpression && callExpression.expression === functionReference) { + return callExpression; + } + break; + // Constructor call (new Foo(...)) + case SyntaxKind.NewExpression: + const newExpression = tryCast(parent, isNewExpression); + if (newExpression && newExpression.expression === functionReference) { + return newExpression; + } + break; + // Method call (x.foo(...)) + case SyntaxKind.PropertyAccessExpression: + const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); + if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionReference) { + const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression); + if (callExpression && callExpression.expression === propertyAccessExpression) { return callExpression; } - break; - // Constructor call (new Foo(...)) - case SyntaxKind.NewExpression: - const newExpression = tryCast(parent, isNewExpression); - if (newExpression && newExpression.expression === functionReference) { - return newExpression; + } + break; + // Method call (x["foo"](...)) + case SyntaxKind.ElementAccessExpression: + const elementAccessExpression = tryCast(parent, isElementAccessExpression); + if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionReference) { + const callExpression = tryCast(elementAccessExpression.parent, isCallExpression); + if (callExpression && callExpression.expression === elementAccessExpression) { + return callExpression; } - break; - // Method call (x.foo(...)) - case SyntaxKind.PropertyAccessExpression: - const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); - if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionReference) { - const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression); - if (callExpression && callExpression.expression === propertyAccessExpression) { - return callExpression; - } - } - break; - // Method call (x["foo"](...)) - case SyntaxKind.ElementAccessExpression: - const elementAccessExpression = tryCast(parent, isElementAccessExpression); - if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionReference) { - const callExpression = tryCast(elementAccessExpression.parent, isCallExpression); - if (callExpression && callExpression.expression === elementAccessExpression) { - return callExpression; - } - } - break; - } + } + break; } - return undefined; } + return undefined; + } - function entryToAccessExpression(entry: FindAllReferences.NodeEntry): ElementAccessExpression | PropertyAccessExpression | undefined { - if (entry.node.parent) { - const reference = entry.node; - const parent = reference.parent; - switch (parent.kind) { - // `C.foo` - case SyntaxKind.PropertyAccessExpression: - const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); - if (propertyAccessExpression && propertyAccessExpression.expression === reference) { - return propertyAccessExpression; - } - break; - // `C["foo"]` - case SyntaxKind.ElementAccessExpression: - const elementAccessExpression = tryCast(parent, isElementAccessExpression); - if (elementAccessExpression && elementAccessExpression.expression === reference) { - return elementAccessExpression; - } - break; - } + function entryToAccessExpression(entry: FindAllReferences.NodeEntry): ElementAccessExpression | PropertyAccessExpression | undefined { + if (entry.node.parent) { + const reference = entry.node; + const parent = reference.parent; + switch (parent.kind) { + // `C.foo` + case SyntaxKind.PropertyAccessExpression: + const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression); + if (propertyAccessExpression && propertyAccessExpression.expression === reference) { + return propertyAccessExpression; + } + break; + // `C["foo"]` + case SyntaxKind.ElementAccessExpression: + const elementAccessExpression = tryCast(parent, isElementAccessExpression); + if (elementAccessExpression && elementAccessExpression.expression === reference) { + return elementAccessExpression; + } + break; } - return undefined; } + return undefined; + } - function entryToType(entry: FindAllReferences.Entry): Node | undefined { - if (entry.kind === FindAllReferences.EntryKind.Node) { - const reference = entry.node; - if (getMeaningFromLocation(reference) === SemanticMeaning.Type || isExpressionWithTypeArgumentsInClassExtendsClause(reference.parent)) { - return reference; - } - } - return undefined; + function entryToType(entry: FindAllReferences.NodeEntry): Node | undefined { + const reference = entry.node; + if (getMeaningFromLocation(reference) === SemanticMeaning.Type || isExpressionWithTypeArgumentsInClassExtendsClause(reference.parent)) { + return reference; } + return undefined; } function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined { @@ -261,18 +259,18 @@ namespace ts.refactor.convertToNamedParameters { return isValidVariableDeclaration(functionDeclaration.parent); } return false; + } - function isValidParameterNodeArray(parameters: NodeArray): parameters is ValidParameterNodeArray { - return getRefactorableParametersLength(parameters) >= minimumParameterLength && every(parameters, isValidParameterDeclaration); - } + function isValidParameterNodeArray(parameters: NodeArray): parameters is ValidParameterNodeArray { + return getRefactorableParametersLength(parameters) >= minimumParameterLength && every(parameters, isValidParameterDeclaration); + } - function isValidParameterDeclaration(paramDeclaration: ParameterDeclaration): paramDeclaration is ValidParameterDeclaration { - return !paramDeclaration.modifiers && !paramDeclaration.decorators && isIdentifier(paramDeclaration.name); - } + function isValidParameterDeclaration(paramDeclaration: ParameterDeclaration): paramDeclaration is ValidParameterDeclaration { + return !paramDeclaration.modifiers && !paramDeclaration.decorators && isIdentifier(paramDeclaration.name); + } - function isValidVariableDeclaration(node: Node): node is ValidVariableDeclaration { - return isVariableDeclaration(node) && isVarConst(node) && isIdentifier(node.name) && !node.type; // TODO: GH#30113 - } + function isValidVariableDeclaration(node: Node): node is ValidVariableDeclaration { + return isVariableDeclaration(node) && isVarConst(node) && isIdentifier(node.name) && !node.type; // TODO: GH#30113 } function hasThisParameter(parameters: NodeArray): boolean { @@ -357,20 +355,6 @@ namespace ts.refactor.convertToNamedParameters { } return createNodeArray([objectParameter]); - function createBindingElementFromParameterDeclaration(parameterDeclaration: ValidParameterDeclaration): BindingElement { - const element = createBindingElement( - /*dotDotDotToken*/ undefined, - /*propertyName*/ undefined, - getParameterName(parameterDeclaration), - isRestParameter(parameterDeclaration) ? createArrayLiteral() : parameterDeclaration.initializer); - - suppressLeadingAndTrailingTrivia(element); - if (parameterDeclaration.initializer && element.initializer) { - copyComments(parameterDeclaration.initializer, element.initializer); - } - return element; - } - function createParameterTypeNode(parameters: NodeArray): TypeLiteralNode { const members = map(parameters, createPropertySignatureFromParameterDeclaration); const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine); @@ -406,6 +390,20 @@ namespace ts.refactor.convertToNamedParameters { } } + function createBindingElementFromParameterDeclaration(parameterDeclaration: ValidParameterDeclaration): BindingElement { + const element = createBindingElement( + /*dotDotDotToken*/ undefined, + /*propertyName*/ undefined, + getParameterName(parameterDeclaration), + isRestParameter(parameterDeclaration) ? createArrayLiteral() : parameterDeclaration.initializer); + + suppressLeadingAndTrailingTrivia(element); + if (parameterDeclaration.initializer && element.initializer) { + copyComments(parameterDeclaration.initializer, element.initializer); + } + return element; + } + function copyComments(sourceNode: Node, targetNode: Node) { const sourceFile = sourceNode.getSourceFile(); const text = sourceFile.text; @@ -416,15 +414,15 @@ namespace ts.refactor.convertToNamedParameters { copyTrailingAsLeadingComments(sourceNode, targetNode, sourceFile); } copyTrailingComments(sourceNode, targetNode, sourceFile); + } - function hasLeadingLineBreak(node: Node, text: string) { - const start = node.getFullStart(); - const end = node.getStart(); - for (let i = start; i < end; i++) { - if (text.charCodeAt(i) === CharacterCodes.lineFeed) return true; - } - return false; + function hasLeadingLineBreak(node: Node, text: string) { + const start = node.getFullStart(); + const end = node.getStart(); + for (let i = start; i < end; i++) { + if (text.charCodeAt(i) === CharacterCodes.lineFeed) return true; } + return false; } function getParameterName(paramDeclaration: ValidParameterDeclaration) { From 6d882513ea8cc33dadab87eccbdaff8475ae218a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 5 Mar 2019 17:45:09 -0800 Subject: [PATCH 75/83] Minor change to heuristic for deferring generic calls --- src/compiler/checker.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad6b87daa1a..c6a866582a4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20900,10 +20900,19 @@ namespace ts { } return resolveErrorCall(node); } - // If we are skipping generic functions (i.e. this call is an argument to another call for which context - // sensitive arguments are being deferred) and every call signature is generic and returns a function type, - // we return resolvingSignature here. This result will be propagated out and turned into anyFunctionType. - if (checkMode & CheckMode.SkipGenericFunctions && callSignatures.every(isGenericFunctionReturningFunction)) { + // When a call to a generic function is an argument to an outer call to a generic function for which + // inference is in process, we have a choice to make. If the inner call relies on inferences made from + // its contextual type to its return type, deferring the inner call processing allows the best possible + // contextual type to accumulate. But if the outer call relies on inferences made from the return type of + // the inner call, the inner call should be processed early. There's no sure way to know which choice is + // right (only a full unification algorithm can determine that), so we resort to the following heuristic: + // If no type arguments are specified in the inner call and at least one call signature is generic and + // returns a function type, we choose to defer processing. This narrowly permits function composition + // operators to flow inferences through return types, but otherwise processes calls right away. We + // use the resolvingSignature singleton to indicate that we deferred processing. This result will be + // propagated out and eventually turned into silentNeverType (a type that is assignable to anything and + // from which we never make inferences). + if (checkMode & CheckMode.SkipGenericFunctions && !node.typeArguments && callSignatures.some(isGenericFunctionReturningFunction)) { skippedGenericFunction(node, checkMode); return resolvingSignature; } @@ -21385,7 +21394,7 @@ namespace ts { if (signature === resolvingSignature) { // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that // returns a function type. We defer checking and return anyFunctionType. - return anyFunctionType; + return silentNeverType; } if (node.expression.kind === SyntaxKind.SuperKeyword) { From b34fe67ac53a59172111a81b60692cf6e27c51e8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 5 Mar 2019 17:49:58 -0800 Subject: [PATCH 76/83] Fix issue of more inferences leading to worse results --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index 622c9de7ba3..7f5772d1489 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1798,7 +1798,7 @@ namespace ts { const span = createTextSpanFromBounds(start, end); const formatContext = formatting.getFormatContext(formatOptions); - return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => { + return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => { cancellationToken.throwIfCancellationRequested(); return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, preferences }); }); From fe9f42480aa3573430f3c3593c4370e4ce9ccc7d Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 6 Mar 2019 13:30:48 -0800 Subject: [PATCH 77/83] Handle error when type parameter of mapped type uses private type Fixes #30201 --- src/compiler/diagnosticMessages.json | 4 ++++ .../transformers/declarations/diagnostics.ts | 4 ++++ ...tMappedPrivateTypeTypeParameter.errors.txt | 15 ++++++++++++++ ...ationEmitMappedPrivateTypeTypeParameter.js | 20 +++++++++++++++++++ ...EmitMappedPrivateTypeTypeParameter.symbols | 16 +++++++++++++++ ...onEmitMappedPrivateTypeTypeParameter.types | 10 ++++++++++ ...ationEmitMappedPrivateTypeTypeParameter.ts | 8 ++++++++ 7 files changed, 77 insertions(+) create mode 100644 tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.errors.txt create mode 100644 tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.js create mode 100644 tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.symbols create mode 100644 tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.types create mode 100644 tests/cases/compiler/declarationEmitMappedPrivateTypeTypeParameter.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 529be375616..4bde97af3e7 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2927,6 +2927,10 @@ "category": "Error", "code": 4102 }, + "Type parameter '{0}' of exported mapped object type is using private name '{1}'.": { + "category": "Error", + "code": 4103 + }, "The current host does not support the '{0}' option.": { "category": "Error", diff --git a/src/compiler/transformers/declarations/diagnostics.ts b/src/compiler/transformers/declarations/diagnostics.ts index 9a9aed99e47..33465799c29 100644 --- a/src/compiler/transformers/declarations/diagnostics.ts +++ b/src/compiler/transformers/declarations/diagnostics.ts @@ -391,6 +391,10 @@ namespace ts { diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1; break; + case SyntaxKind.MappedType: + diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_mapped_object_type_is_using_private_name_1; + break; + case SyntaxKind.ConstructorType: case SyntaxKind.ConstructSignature: diagnosticMessage = Diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1; diff --git a/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.errors.txt b/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.errors.txt new file mode 100644 index 00000000000..cb7c4f760ad --- /dev/null +++ b/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.errors.txt @@ -0,0 +1,15 @@ +/FromFactor.ts(2,15): error TS2304: Cannot find name 'StringKeyOf'. +/FromFactor.ts(2,15): error TS4103: Type parameter 'TName' of exported mapped object type is using private name 'StringKeyOf'. + + +==== /Helpers.ts (0 errors) ==== + export type StringKeyOf = Extract; + +==== /FromFactor.ts (2 errors) ==== + export type RowToColumns = { + [TName in StringKeyOf]: any; + ~~~~~~~~~~~ +!!! error TS2304: Cannot find name 'StringKeyOf'. + ~~~~~~~~~~~ +!!! error TS4103: Type parameter 'TName' of exported mapped object type is using private name 'StringKeyOf'. + } \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.js b/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.js new file mode 100644 index 00000000000..79debe1b4b8 --- /dev/null +++ b/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.js @@ -0,0 +1,20 @@ +//// [tests/cases/compiler/declarationEmitMappedPrivateTypeTypeParameter.ts] //// + +//// [Helpers.ts] +export type StringKeyOf = Extract; + +//// [FromFactor.ts] +export type RowToColumns = { + [TName in StringKeyOf]: any; +} + +//// [Helpers.js] +"use strict"; +exports.__esModule = true; +//// [FromFactor.js] +"use strict"; +exports.__esModule = true; + + +//// [Helpers.d.ts] +export declare type StringKeyOf = Extract; diff --git a/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.symbols b/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.symbols new file mode 100644 index 00000000000..073e34762cd --- /dev/null +++ b/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.symbols @@ -0,0 +1,16 @@ +=== /Helpers.ts === +export type StringKeyOf = Extract; +>StringKeyOf : Symbol(StringKeyOf, Decl(Helpers.ts, 0, 0)) +>TObj : Symbol(TObj, Decl(Helpers.ts, 0, 24)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>TObj : Symbol(TObj, Decl(Helpers.ts, 0, 24)) + +=== /FromFactor.ts === +export type RowToColumns = { +>RowToColumns : Symbol(RowToColumns, Decl(FromFactor.ts, 0, 0)) +>TColumns : Symbol(TColumns, Decl(FromFactor.ts, 0, 25)) + + [TName in StringKeyOf]: any; +>TName : Symbol(TName, Decl(FromFactor.ts, 1, 5)) +>TColumns : Symbol(TColumns, Decl(FromFactor.ts, 0, 25)) +} diff --git a/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.types b/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.types new file mode 100644 index 00000000000..142549c4d01 --- /dev/null +++ b/tests/baselines/reference/declarationEmitMappedPrivateTypeTypeParameter.types @@ -0,0 +1,10 @@ +=== /Helpers.ts === +export type StringKeyOf = Extract; +>StringKeyOf : Extract + +=== /FromFactor.ts === +export type RowToColumns = { +>RowToColumns : RowToColumns + + [TName in StringKeyOf]: any; +} diff --git a/tests/cases/compiler/declarationEmitMappedPrivateTypeTypeParameter.ts b/tests/cases/compiler/declarationEmitMappedPrivateTypeTypeParameter.ts new file mode 100644 index 00000000000..b65480ea89b --- /dev/null +++ b/tests/cases/compiler/declarationEmitMappedPrivateTypeTypeParameter.ts @@ -0,0 +1,8 @@ +// @declaration: true +// @filename: /Helpers.ts +export type StringKeyOf = Extract; + +// @filename: /FromFactor.ts +export type RowToColumns = { + [TName in StringKeyOf]: any; +} \ No newline at end of file From 79a1f297278cd35705c7640e11a0e9307504a221 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 6 Mar 2019 14:46:14 -0800 Subject: [PATCH 78/83] In recursive directory watching ignore folders and files in node_modules starting with "." Fixes #30004 --- src/compiler/sys.ts | 14 ++++- .../unittests/tsserver/watchEnvironment.ts | 59 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 6d8c4628092..487ea103840 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -371,6 +371,8 @@ namespace ts { else { directoryWatcher = { watcher: host.watchDirectory(dirName, fileName => { + if (isInNodeModulesStartingWithDot(fileName)) return; + // Call the actual callback callbackCache.forEach((callbacks, rootDirName) => { if (rootDirName === dirPath || (startsWith(dirPath, rootDirName) && dirPath[rootDirName.length] === directorySeparator)) { @@ -426,7 +428,7 @@ namespace ts { const childFullName = getNormalizedAbsolutePath(child, parentDir); // Filter our the symbolic link directories since those arent included in recursive watch // which is same behaviour when recursive: true is passed to fs.watch - return filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined; + return !isInNodeModulesStartingWithDot(childFullName) && filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined; }) : emptyArray, existingChildWatches, (child, childWatcher) => filePathComparer(child, childWatcher.dirName), @@ -452,6 +454,16 @@ namespace ts { (newChildWatches || (newChildWatches = [])).push(childWatcher); } } + + function isInNodeModulesStartingWithDot(path: string) { + return isInPath(path, "/node_modules/."); + } + + function isInPath(path: string, searchPath: string) { + if (stringContains(path, searchPath)) return true; + if (host.useCaseSensitiveFileNames) return false; + return stringContains(toCanonicalFilePath(path), searchPath); + } } // TODO: GH#18217 Methods on System are often used as if they are certainly defined diff --git a/src/testRunner/unittests/tsserver/watchEnvironment.ts b/src/testRunner/unittests/tsserver/watchEnvironment.ts index 2c195e0c7d2..43090f0970a 100644 --- a/src/testRunner/unittests/tsserver/watchEnvironment.ts +++ b/src/testRunner/unittests/tsserver/watchEnvironment.ts @@ -132,4 +132,63 @@ namespace ts.projectSystem { verifyRootedDirectoryWatch("c:/users/username/"); }); }); + + it(`unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem recursive watch directory implementation does not watch files/directories in node_modules starting with "."`, () => { + const projectFolder = "/a/username/project"; + const projectSrcFolder = `${projectFolder}/src`; + const configFile: File = { + path: `${projectFolder}/tsconfig.json`, + content: "{}" + }; + const index: File = { + path: `${projectSrcFolder}/index.ts`, + content: `import {} from "file"` + }; + const file1: File = { + path: `${projectSrcFolder}/file1.ts`, + content: "" + }; + const nodeModulesExistingUnusedFile: File = { + path: `${projectFolder}/node_modules/someFile.d.ts`, + content: "" + }; + + const fileNames = [index, file1, configFile, libFile].map(file => file.path); + // All closed files(files other than index), project folder, project/src folder and project/node_modules/@types folder + const expectedWatchedFiles = arrayToMap(fileNames.slice(1), identity, () => 1); + const expectedWatchedDirectories = arrayToMap([projectFolder, projectSrcFolder, `${projectFolder}/${nodeModules}`, `${projectFolder}/${nodeModulesAtTypes}`], identity, () => 1); + + const environmentVariables = createMap(); + environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory); + const host = createServerHost([index, file1, configFile, libFile, nodeModulesExistingUnusedFile], { environmentVariables }); + const projectService = createProjectService(host); + projectService.openClientFile(index.path); + + const project = Debug.assertDefined(projectService.configuredProjects.get(configFile.path)); + verifyProject(); + + const nodeModulesIgnoredFileFromIgnoreDirectory: File = { + path: `${projectFolder}/node_modules/.cache/someFile.d.ts`, + content: "" + }; + host.ensureFileOrFolder(nodeModulesIgnoredFileFromIgnoreDirectory); + host.checkTimeoutQueueLength(0); + verifyProject(); + + const nodeModulesIgnoredFile: File = { + path: `${projectFolder}/node_modules/.cacheFile.ts`, + content: "" + }; + host.ensureFileOrFolder(nodeModulesIgnoredFile); + host.checkTimeoutQueueLength(0); + verifyProject(); + + function verifyProject() { + checkWatchedDirectories(host, emptyArray, /*recursive*/ true); + checkWatchedFilesDetailed(host, expectedWatchedFiles); + checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, /*recursive*/ false); + checkProjectActualFiles(project, fileNames); + } + }); + } From ec2ee9ff3afdf4089b71e06dfc64465d1ee210fd Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 6 Mar 2019 15:00:26 -0800 Subject: [PATCH 79/83] Ignore anything starting with ".git" Fixes #29782 --- src/compiler/resolutionCache.ts | 6 +++--- src/compiler/sys.ts | 11 +++++++---- src/compiler/watch.ts | 2 +- src/server/editorServices.ts | 4 ++-- .../unittests/tsserver/watchEnvironment.ts | 16 ++++++++++++++++ 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 237c1637d31..b82a5a78d71 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -71,8 +71,8 @@ namespace ts { nonRecursive?: boolean; } - export function isPathInNodeModulesStartingWithDot(path: Path) { - return stringContains(path, "/node_modules/."); + export function isPathIgnored(path: Path) { + return some(ignoredPaths, searchPath => stringContains(path, searchPath)); } export const maxNumberOfFilesToIterateForInvalidation = 256; @@ -696,7 +696,7 @@ namespace ts { } else { // If something to do with folder/file starting with "." in node_modules folder, skip it - if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return false; + if (isPathIgnored(fileOrDirectoryPath)) return false; // Some file or directory in the watching directory is created // Return early if it does not have any of the watching extension or not the custom failed lookup path diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 487ea103840..f38d0ae506c 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -326,6 +326,9 @@ namespace ts { : FileWatcherEventKind.Changed; } + /*@internal*/ + export const ignoredPaths = ["/node_modules/.", "/.git"]; + /*@internal*/ export interface RecursiveDirectoryWatcherHost { watchDirectory: HostWatchDirectory; @@ -371,7 +374,7 @@ namespace ts { else { directoryWatcher = { watcher: host.watchDirectory(dirName, fileName => { - if (isInNodeModulesStartingWithDot(fileName)) return; + if (isIgnoredPath(fileName)) return; // Call the actual callback callbackCache.forEach((callbacks, rootDirName) => { @@ -428,7 +431,7 @@ namespace ts { const childFullName = getNormalizedAbsolutePath(child, parentDir); // Filter our the symbolic link directories since those arent included in recursive watch // which is same behaviour when recursive: true is passed to fs.watch - return !isInNodeModulesStartingWithDot(childFullName) && filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined; + return !isIgnoredPath(childFullName) && filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined; }) : emptyArray, existingChildWatches, (child, childWatcher) => filePathComparer(child, childWatcher.dirName), @@ -455,8 +458,8 @@ namespace ts { } } - function isInNodeModulesStartingWithDot(path: string) { - return isInPath(path, "/node_modules/."); + function isIgnoredPath(path: string) { + return some(ignoredPaths, searchPath => isInPath(path, searchPath)); } function isInPath(path: string, searchPath: string) { diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 827a3279b3b..fa2a06e4ed0 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -987,7 +987,7 @@ namespace ts { } nextSourceFileVersion(fileOrDirectoryPath); - if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return; + if (isPathIgnored(fileOrDirectoryPath)) return; // If the the added or created file or directory is not supported file name, ignore the file // But when watched directory is added/removed, we need to reload the file list diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 10939b68114..56ae6dfdc22 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1002,7 +1002,7 @@ namespace ts.server { fileOrDirectory => { const fileOrDirectoryPath = this.toPath(fileOrDirectory); project.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); - if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return; + if (isPathIgnored(fileOrDirectoryPath)) return; const configFilename = project.getConfigFilePath(); // If the the added or created file or directory is not supported file name, ignore the file @@ -2071,7 +2071,7 @@ namespace ts.server { watchDir, (fileOrDirectory) => { const fileOrDirectoryPath = this.toPath(fileOrDirectory); - if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return; + if (isPathIgnored(fileOrDirectoryPath)) return; // Has extension Debug.assert(result.refCount > 0); diff --git a/src/testRunner/unittests/tsserver/watchEnvironment.ts b/src/testRunner/unittests/tsserver/watchEnvironment.ts index 43090f0970a..bc8e84a8025 100644 --- a/src/testRunner/unittests/tsserver/watchEnvironment.ts +++ b/src/testRunner/unittests/tsserver/watchEnvironment.ts @@ -183,6 +183,22 @@ namespace ts.projectSystem { host.checkTimeoutQueueLength(0); verifyProject(); + const gitIgnoredFileFromIgnoreDirectory: File = { + path: `${projectFolder}/.git/someFile.d.ts`, + content: "" + }; + host.ensureFileOrFolder(gitIgnoredFileFromIgnoreDirectory); + host.checkTimeoutQueueLength(0); + verifyProject(); + + const gitIgnoredFile: File = { + path: `${projectFolder}/.gitCache.d.ts`, + content: "" + }; + host.ensureFileOrFolder(gitIgnoredFile); + host.checkTimeoutQueueLength(0); + verifyProject(); + function verifyProject() { checkWatchedDirectories(host, emptyArray, /*recursive*/ true); checkWatchedFilesDetailed(host, expectedWatchedFiles); From a9e924b6f0edb02c719a77f6d11fbae4704574e7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 7 Mar 2019 07:06:48 -0800 Subject: [PATCH 80/83] Fix check for function type (allow checked type to have overloads) --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c6a866582a4..3fa48688201 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20925,7 +20925,7 @@ namespace ts { } function isGenericFunctionReturningFunction(signature: Signature) { - return !!(signature.typeParameters && getSingleCallSignature(getReturnTypeOfSignature(signature))); + return !!(signature.typeParameters && isFunctionType(getReturnTypeOfSignature(signature))); } /** From 4c9ad086109d80a3956e6b66efc8728b706b9d87 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 7 Mar 2019 13:34:28 -0800 Subject: [PATCH 81/83] Add fix for webpack history merge bug (#29339) * Add fix for webpack history merge bug * Add test case --- src/compiler/checker.ts | 2 +- .../noCrashUMDMergedWithGlobalValue.js | 14 ++++++++++++++ .../noCrashUMDMergedWithGlobalValue.symbols | 18 ++++++++++++++++++ .../noCrashUMDMergedWithGlobalValue.types | 15 +++++++++++++++ .../noCrashUMDMergedWithGlobalValue.ts | 9 +++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/noCrashUMDMergedWithGlobalValue.js create mode 100644 tests/baselines/reference/noCrashUMDMergedWithGlobalValue.symbols create mode 100644 tests/baselines/reference/noCrashUMDMergedWithGlobalValue.types create mode 100644 tests/cases/compiler/noCrashUMDMergedWithGlobalValue.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 179e5fb04d0..74545e366fd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8790,7 +8790,7 @@ namespace ts { function getTypeReferenceTypeWorker(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined): Type | undefined { if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - if (symbol.valueDeclaration && isBinaryExpression(symbol.valueDeclaration.parent)) { + if (symbol.valueDeclaration && symbol.valueDeclaration.parent && isBinaryExpression(symbol.valueDeclaration.parent)) { const jsdocType = getJSDocTypeReference(node, symbol, typeArguments); if (jsdocType) { return jsdocType; diff --git a/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.js b/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.js new file mode 100644 index 00000000000..7f8edd9c387 --- /dev/null +++ b/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.js @@ -0,0 +1,14 @@ +//// [tests/cases/compiler/noCrashUMDMergedWithGlobalValue.ts] //// + +//// [other.d.ts] +export as namespace SomeInterface; +export type Action = "PUSH" | "POP" | "REPLACE"; + +//// [main.ts] +interface SomeInterface { + readonly length: number; +} +declare const value: SomeInterface; + + +//// [main.js] diff --git a/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.symbols b/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.symbols new file mode 100644 index 00000000000..c2249abe1d4 --- /dev/null +++ b/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.symbols @@ -0,0 +1,18 @@ +=== /other.d.ts === +export as namespace SomeInterface; +>SomeInterface : Symbol(SomeInterface, Decl(other.d.ts, 0, 0)) + +export type Action = "PUSH" | "POP" | "REPLACE"; +>Action : Symbol(Action, Decl(other.d.ts, 0, 34)) + +=== /main.ts === +interface SomeInterface { +>SomeInterface : Symbol("/other", Decl(other.d.ts, 0, 0), Decl(main.ts, 0, 0)) + + readonly length: number; +>length : Symbol(length, Decl(main.ts, 0, 25)) +} +declare const value: SomeInterface; +>value : Symbol(value, Decl(main.ts, 3, 13)) +>SomeInterface : Symbol("/other", Decl(other.d.ts, 0, 0), Decl(main.ts, 0, 0)) + diff --git a/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.types b/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.types new file mode 100644 index 00000000000..f01fad2560e --- /dev/null +++ b/tests/baselines/reference/noCrashUMDMergedWithGlobalValue.types @@ -0,0 +1,15 @@ +=== /other.d.ts === +export as namespace SomeInterface; +>SomeInterface : typeof import("/other") + +export type Action = "PUSH" | "POP" | "REPLACE"; +>Action : Action + +=== /main.ts === +interface SomeInterface { + readonly length: number; +>length : number +} +declare const value: SomeInterface; +>value : import("/other") + diff --git a/tests/cases/compiler/noCrashUMDMergedWithGlobalValue.ts b/tests/cases/compiler/noCrashUMDMergedWithGlobalValue.ts new file mode 100644 index 00000000000..0e37eecf91d --- /dev/null +++ b/tests/cases/compiler/noCrashUMDMergedWithGlobalValue.ts @@ -0,0 +1,9 @@ +//@filename: /other.d.ts +export as namespace SomeInterface; +export type Action = "PUSH" | "POP" | "REPLACE"; + +//@filename: /main.ts +interface SomeInterface { + readonly length: number; +} +declare const value: SomeInterface; From e982240500a527486de1693cd6eeb70c3615c5bb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 7 Mar 2019 17:39:58 -0800 Subject: [PATCH 82/83] Instantiating a signature in the context of another should infer from return type predicates if they match up (#30242) * Instantiating a signature in the context of another should infer from return type predicates if they match up * Invert condition per PR feedback --- src/compiler/checker.ts | 6 +++ ...PredicateIsInstantiateInContextOfTarget.js | 49 +++++++++++++++++++ ...cateIsInstantiateInContextOfTarget.symbols | 42 ++++++++++++++++ ...dicateIsInstantiateInContextOfTarget.types | 40 +++++++++++++++ ...redicateIsInstantiateInContextOfTarget.tsx | 16 ++++++ 5 files changed, 153 insertions(+) create mode 100644 tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.js create mode 100644 tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.symbols create mode 100644 tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.types create mode 100644 tests/cases/compiler/returnTypePredicateIsInstantiateInContextOfTarget.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 74545e366fd..678a3fc95c1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20042,6 +20042,12 @@ namespace ts { }); if (!contextualMapper) { inferTypes(context.inferences, getReturnTypeOfSignature(contextualSignature), getReturnTypeOfSignature(signature), InferencePriority.ReturnType); + const signaturePredicate = getTypePredicateOfSignature(signature); + const contextualPredicate = getTypePredicateOfSignature(sourceSignature); + if (signaturePredicate && contextualPredicate && signaturePredicate.kind === contextualPredicate.kind && + (signaturePredicate.kind === TypePredicateKind.This || signaturePredicate.parameterIndex === (contextualPredicate as IdentifierTypePredicate).parameterIndex)) { + inferTypes(context.inferences, contextualPredicate.type, signaturePredicate.type, InferencePriority.ReturnType); + } } return getSignatureInstantiation(signature, getInferredTypes(context), isInJSFile(contextualSignature.declaration)); } diff --git a/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.js b/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.js new file mode 100644 index 00000000000..8428f81bd05 --- /dev/null +++ b/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.js @@ -0,0 +1,49 @@ +//// [returnTypePredicateIsInstantiateInContextOfTarget.tsx] +/// +import * as React from "react"; +class TestComponent extends React.Component<{ isAny: (obj: any) => obj is T }> { + static defaultProps = { + isAny: TestComponent.isAny + } + + // Type guard is defined as a static class property + static isAny(obj: any): obj is T { + return true; + } +} + +const TestRender = () => ; + +//// [returnTypePredicateIsInstantiateInContextOfTarget.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +/// +var React = require("react"); +var TestComponent = /** @class */ (function (_super) { + __extends(TestComponent, _super); + function TestComponent() { + return _super !== null && _super.apply(this, arguments) || this; + } + // Type guard is defined as a static class property + TestComponent.isAny = function (obj) { + return true; + }; + TestComponent.defaultProps = { + isAny: TestComponent.isAny + }; + return TestComponent; +}(React.Component)); +var TestRender = function () { return React.createElement(TestComponent, null); }; diff --git a/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.symbols b/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.symbols new file mode 100644 index 00000000000..9c7558aa642 --- /dev/null +++ b/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.symbols @@ -0,0 +1,42 @@ +=== tests/cases/compiler/returnTypePredicateIsInstantiateInContextOfTarget.tsx === +/// +import * as React from "react"; +>React : Symbol(React, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 1, 6)) + +class TestComponent extends React.Component<{ isAny: (obj: any) => obj is T }> { +>TestComponent : Symbol(TestComponent, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 1, 31)) +>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94)) +>React : Symbol(React, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 1, 6)) +>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94)) +>isAny : Symbol(isAny, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 2, 45)) +>T : Symbol(T, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 2, 54)) +>obj : Symbol(obj, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 2, 57)) +>obj : Symbol(obj, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 2, 57)) +>T : Symbol(T, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 2, 54)) + + static defaultProps = { +>defaultProps : Symbol(TestComponent.defaultProps, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 2, 83)) + + isAny: TestComponent.isAny +>isAny : Symbol(isAny, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 3, 27)) +>TestComponent.isAny : Symbol(TestComponent.isAny, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 5, 5)) +>TestComponent : Symbol(TestComponent, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 1, 31)) +>isAny : Symbol(TestComponent.isAny, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 5, 5)) + } + + // Type guard is defined as a static class property + static isAny(obj: any): obj is T { +>isAny : Symbol(TestComponent.isAny, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 5, 5)) +>T : Symbol(T, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 8, 17)) +>obj : Symbol(obj, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 8, 20)) +>obj : Symbol(obj, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 8, 20)) +>T : Symbol(T, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 8, 17)) + + return true; + } +} + +const TestRender = () => ; +>TestRender : Symbol(TestRender, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 13, 5)) +>TestComponent : Symbol(TestComponent, Decl(returnTypePredicateIsInstantiateInContextOfTarget.tsx, 1, 31)) + diff --git a/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.types b/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.types new file mode 100644 index 00000000000..4780b2722e5 --- /dev/null +++ b/tests/baselines/reference/returnTypePredicateIsInstantiateInContextOfTarget.types @@ -0,0 +1,40 @@ +=== tests/cases/compiler/returnTypePredicateIsInstantiateInContextOfTarget.tsx === +/// +import * as React from "react"; +>React : typeof React + +class TestComponent extends React.Component<{ isAny: (obj: any) => obj is T }> { +>TestComponent : TestComponent +>React.Component : React.Component<{ isAny: (obj: any) => obj is T; }, {}, any> +>React : typeof React +>Component : typeof React.Component +>isAny : (obj: any) => obj is T +>obj : any + + static defaultProps = { +>defaultProps : { isAny: (obj: any) => obj is T; } +>{ isAny: TestComponent.isAny } : { isAny: (obj: any) => obj is T; } + + isAny: TestComponent.isAny +>isAny : (obj: any) => obj is T +>TestComponent.isAny : (obj: any) => obj is T +>TestComponent : typeof TestComponent +>isAny : (obj: any) => obj is T + } + + // Type guard is defined as a static class property + static isAny(obj: any): obj is T { +>isAny : (obj: any) => obj is T +>obj : any + + return true; +>true : true + } +} + +const TestRender = () => ; +>TestRender : () => JSX.Element +>() => : () => JSX.Element +> : JSX.Element +>TestComponent : typeof TestComponent + diff --git a/tests/cases/compiler/returnTypePredicateIsInstantiateInContextOfTarget.tsx b/tests/cases/compiler/returnTypePredicateIsInstantiateInContextOfTarget.tsx new file mode 100644 index 00000000000..8a27e3fde7c --- /dev/null +++ b/tests/cases/compiler/returnTypePredicateIsInstantiateInContextOfTarget.tsx @@ -0,0 +1,16 @@ +// @jsx: react +// @strict: true +/// +import * as React from "react"; +class TestComponent extends React.Component<{ isAny: (obj: any) => obj is T }> { + static defaultProps = { + isAny: TestComponent.isAny + } + + // Type guard is defined as a static class property + static isAny(obj: any): obj is T { + return true; + } +} + +const TestRender = () => ; \ No newline at end of file From a887c6ba4077eb51ddbc5a8ee9a3bab29bd5c4b7 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 8 Mar 2019 10:34:35 -0800 Subject: [PATCH 83/83] Remove unused (and sometimes broken) targets and scripts (#30054) * Remove unused (and sometimes broken) targets and scripts * Remove browser-specific harness code --- Gulpfile.js | 51 +- scripts/browserify-optional.js | 24 - scripts/build/browserify.js | 33 - scripts/build/sourcemaps.js | 98 - scripts/createBenchmark.ts | 124 - scripts/ior.ts | 125 - scripts/parallel-lint.js | 50 - scripts/types/ambient.d.ts | 8 - scripts/types/mocha/LICENSE | 21 - scripts/types/mocha/index.d.ts | 2856 ----------------- .../types/mocha/lib/interfaces/common.d.ts | 109 - scripts/types/mocha/lib/ms.d.ts | 17 - scripts/types/mocha/package.json | 5 - src/harness/harness.ts | 381 +-- src/testRunner/parallel/host.ts | 36 +- src/testRunner/runner.ts | 19 +- tests/perfsys.ts | 104 - tests/perftsc.ts | 30 - tests/perftsc.tsconfig.json | 15 - tests/test.bat | 13 - tests/webTestResults.html | 42 - tests/webTestServer.ts | 1014 ------ tests/webTestServer.tsconfig.json | 14 - tests/webhost/favicon-32x32.png | Bin 800 -> 0 bytes tests/webhost/webhost.html | 87 - tests/webhost/webtsc.ts | 104 - tests/webhost/webtsc.tsconfig.json | 14 - tests/webhost/wscript.js | 22 - 28 files changed, 34 insertions(+), 5382 deletions(-) delete mode 100644 scripts/browserify-optional.js delete mode 100644 scripts/build/browserify.js delete mode 100644 scripts/createBenchmark.ts delete mode 100644 scripts/ior.ts delete mode 100644 scripts/parallel-lint.js delete mode 100644 scripts/types/mocha/LICENSE delete mode 100644 scripts/types/mocha/index.d.ts delete mode 100644 scripts/types/mocha/lib/interfaces/common.d.ts delete mode 100644 scripts/types/mocha/lib/ms.d.ts delete mode 100644 scripts/types/mocha/package.json delete mode 100644 tests/perfsys.ts delete mode 100644 tests/perftsc.ts delete mode 100644 tests/perftsc.tsconfig.json delete mode 100644 tests/test.bat delete mode 100644 tests/webTestResults.html delete mode 100644 tests/webTestServer.ts delete mode 100644 tests/webTestServer.tsconfig.json delete mode 100644 tests/webhost/favicon-32x32.png delete mode 100644 tests/webhost/webhost.html delete mode 100644 tests/webhost/webtsc.ts delete mode 100644 tests/webhost/webtsc.tsconfig.json delete mode 100644 tests/webhost/wscript.js diff --git a/Gulpfile.js b/Gulpfile.js index 3ce488caedd..f3f1d7ae7a6 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -12,10 +12,9 @@ const merge2 = require("merge2"); const mkdirp = require("mkdirp"); const { src, dest, task, parallel, series, watch } = require("gulp"); const { append, transform } = require("gulp-insert"); -const { browserify } = require("./scripts/build/browserify"); const { prependFile } = require("./scripts/build/prepend"); const { exec, readJson, needsUpdate, getDiffTool, getDirSize, flatten, rm } = require("./scripts/build/utils"); -const { runConsoleTests, cleanTestDirs, writeTestConfigFile, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } = require("./scripts/build/tests"); +const { runConsoleTests, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } = require("./scripts/build/tests"); const { buildProject, cleanProject, watchProject } = require("./scripts/build/projects"); const cmdLineOptions = require("./scripts/build/options"); @@ -454,44 +453,6 @@ task("runtests-parallel").flags = { " --built": "Compile using the built version of the compiler.", }; -const buildWebTestServer = () => buildProject("tests/webTestServer.tsconfig.json"); -const cleanWebTestServer = () => cleanProject("tests/webTestServer.tsconfig.json"); -cleanTasks.push(cleanWebTestServer); - -const browserifyTests = () => src(["built/local/run.js"], { base: "built/local" }) - .pipe(newer("built/local/bundle.js")) - .pipe(sourcemaps.init({ loadMaps: true })) - .pipe(browserify()) - .pipe(rename("bundle.js")) - .pipe(sourcemaps.write(".", /**@type {*}*/({ includeContent: false, destPath: "built/local" }))) - .pipe(dest("built/local")); - -const runtestsBrowser = async () => { - await cleanTestDirs(); - const { tests, runners, light } = cmdLineOptions; - const testConfigFile = "test.config"; - await del([testConfigFile]); - if (tests || runners || light) { - writeTestConfigFile(tests, runners, light); - } - const args = ["tests/webTestServer.js"]; - if (cmdLineOptions.browser) { - args.push(cmdLineOptions.browser); - } - if (tests) { - args.push(JSON.stringify(tests)); - } - await exec(process.execPath, args); -}; - -task("runtests-browser", series(preBuild, parallel(buildTests, buildServices, buildLssl, buildWebTestServer), browserifyTests, runtestsBrowser)); -task("runtests-browser").description = "Runs the tests using the built run.js file like 'gulp runtests'."; -task("runtests-browser").flags = { - "-t --tests=": "pattern for tests to run", - "-b --browser=": "Either 'IE' or 'chrome'", - " --built": "Compile using the built version of the compiler.", -}; - task("diff", () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true })); task("diff").description = "Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable"; @@ -525,16 +486,6 @@ cleanTasks.push(cleanWebHost); task("clean-webhost", cleanWebHost); task("clean-webhost").description = "Cleans the outputs of the tsc web host"; -// TODO(rbuckton): Determine if 'perftsc' is still in use. -const buildPerfTsc = () => buildProject("tests/perftsc.tsconfig.json"); -task("perftsc", series(lkgPreBuild, buildPerfTsc)); -task("perftsc").description = "Builds augmented version of the compiler for perf tests"; - -const cleanPerfTsc = () => cleanProject("tests/perftsc.tsconfig.json"); -cleanTasks.push(cleanPerfTsc); -task("clean-perftsc", cleanPerfTsc); -task("clean-perftsc").description = "Cleans the outputs of the perftsc project"; - const buildLoggedIO = async () => { mkdirp.sync("built/local/temp"); await exec(process.execPath, ["lib/tsc", "--types", "--target", "es5", "--lib", "es5", "--outdir", "built/local/temp", "src/harness/loggedIO.ts"]); diff --git a/scripts/browserify-optional.js b/scripts/browserify-optional.js deleted file mode 100644 index 43997c7803c..00000000000 --- a/scripts/browserify-optional.js +++ /dev/null @@ -1,24 +0,0 @@ -// simple script to optionally elide source-map-support (or other optional modules) when running browserify. - -var stream = require("stream"), - Transform = stream.Transform, - resolve = require("browser-resolve"); - -var requirePattern = /require\s*\(\s*['"](source-map-support)['"]\s*\)/; -module.exports = function (file) { - return new Transform({ - transform: function (data, encoding, cb) { - var text = encoding === "buffer" ? data.toString("utf8") : data; - this.push(new Buffer(text.replace(requirePattern, function (originalText, moduleName) { - try { - resolve.sync(moduleName, { filename: file }); - return originalText; - } - catch (e) { - return "(function () { throw new Error(\"module '" + moduleName + "' not found.\"); })()"; - } - }), "utf8")); - cb(); - } - }); -}; \ No newline at end of file diff --git a/scripts/build/browserify.js b/scripts/build/browserify.js deleted file mode 100644 index ba8ccd15824..00000000000 --- a/scripts/build/browserify.js +++ /dev/null @@ -1,33 +0,0 @@ -// @ts-check -const browserify = require("browserify"); -const Vinyl = require("vinyl"); -const { Transform } = require("stream"); -const { streamFromFile } = require("./utils"); -const { replaceContents } = require("./sourcemaps"); - -/** - * @param {import("browserify").Options} [opts] - */ -function browserifyFile(opts) { - return new Transform({ - objectMode: true, - /** - * @param {string | Buffer | Vinyl} input - */ - transform(input, _, cb) { - if (typeof input === "string" || Buffer.isBuffer(input)) return cb(new Error("Only Vinyl files are supported.")); - try { - browserify(Object.assign({}, opts, { debug: !!input.sourceMap, basedir: input.base })) - .add(streamFromFile(input), { file: input.path, basedir: input.base }) - .bundle((err, contents) => { - if (err) return cb(err); - cb(null, replaceContents(input, contents)); - }); - } - catch (e) { - cb(e); - } - } - }); -} -exports.browserify = browserifyFile; \ No newline at end of file diff --git a/scripts/build/sourcemaps.js b/scripts/build/sourcemaps.js index 2c85b897b09..24c13b941c9 100644 --- a/scripts/build/sourcemaps.js +++ b/scripts/build/sourcemaps.js @@ -1,102 +1,4 @@ // @ts-check -/// - -const path = require("path"); -const convertMap = require("convert-source-map"); -const applySourceMap = require("vinyl-sourcemaps-apply"); -const through2 = require("through2"); - -/** - * @param {import("vinyl")} input - * @param {string | Buffer} contents - * @param {string | RawSourceMap} [sourceMap] - */ -function replaceContents(input, contents, sourceMap) { - const output = input.clone(); - output.contents = typeof contents === "string" ? Buffer.from(contents, "utf8") : contents; - if (input.sourceMap) { - output.sourceMap = typeof input.sourceMap === "string" ? /**@type {RawSourceMap}*/(JSON.parse(input.sourceMap)) : input.sourceMap; - if (typeof sourceMap === "string") { - sourceMap = /** @type {RawSourceMap} */(JSON.parse(sourceMap)); - } - else if (sourceMap === undefined) { - const stringContents = typeof contents === "string" ? contents : contents.toString("utf8"); - const newSourceMapConverter = convertMap.fromSource(stringContents); - if (newSourceMapConverter) { - sourceMap = /** @type {RawSourceMap} */(newSourceMapConverter.toObject()); - output.contents = new Buffer(convertMap.removeMapFileComments(stringContents), "utf8"); - } - } - if (sourceMap) { - const cwd = input.cwd || process.cwd(); - const base = input.base || cwd; - const sourceRoot = output.sourceMap.sourceRoot; - makeAbsoluteSourceMap(cwd, base, output.sourceMap); - makeAbsoluteSourceMap(cwd, base, /** @type {RawSourceMap} */(sourceMap)); - applySourceMap(output, sourceMap); - makeRelativeSourceMap(cwd, base, sourceRoot, output.sourceMap); - } - else { - output.sourceMap = undefined; - } - } - return output; -} -exports.replaceContents = replaceContents; - -function removeSourceMaps() { - return through2.obj((/**@type {import("vinyl")}*/file, _, cb) => { - if (file.isBuffer()) { - file.contents = Buffer.from(convertMap.removeMapFileComments(file.contents.toString("utf8")), "utf8"); - if (file.sourceMap) { - file.sourceMap = undefined; - } - } - cb(null, file); - }); -} -exports.removeSourceMaps = removeSourceMaps; - -/** - * @param {string | undefined} cwd - * @param {string | undefined} base - * @param {RawSourceMap} sourceMap - * - * @typedef {object} RawSourceMap - * @property {string} version - * @property {string} file - * @property {string} [sourceRoot] - * @property {string[]} sources - * @property {string[]} [sourcesContent] - * @property {string} mappings - * @property {string[]} [names] - */ -function makeAbsoluteSourceMap(cwd = process.cwd(), base = "", sourceMap) { - const sourceRoot = sourceMap.sourceRoot || ""; - const resolvedBase = path.resolve(cwd, base); - const resolvedSourceRoot = path.resolve(resolvedBase, sourceRoot); - sourceMap.file = path.resolve(resolvedBase, sourceMap.file).replace(/\\/g, "/"); - sourceMap.sources = sourceMap.sources.map(source => path.resolve(resolvedSourceRoot, source).replace(/\\/g, "/")); - sourceMap.sourceRoot = ""; -} -exports.makeAbsoluteSourceMap = makeAbsoluteSourceMap; - -/** - * @param {string | undefined} cwd - * @param {string | undefined} base - * @param {string} sourceRoot - * @param {RawSourceMap} sourceMap - */ -function makeRelativeSourceMap(cwd = process.cwd(), base = "", sourceRoot, sourceMap) { - makeAbsoluteSourceMap(cwd, base, sourceMap); - const resolvedBase = path.resolve(cwd, base); - const resolvedSourceRoot = path.resolve(resolvedBase, sourceRoot); - sourceMap.file = path.relative(resolvedBase, sourceMap.file).replace(/\\/g, "/"); - sourceMap.sources = sourceMap.sources.map(source => path.relative(resolvedSourceRoot, source).replace(/\\/g, "/")); - sourceMap.sourceRoot = sourceRoot; -} -exports.makeRelativeSourceMap = makeRelativeSourceMap; - /** * @param {string} message * @returns {never} diff --git a/scripts/createBenchmark.ts b/scripts/createBenchmark.ts deleted file mode 100644 index 254c0d99af0..00000000000 --- a/scripts/createBenchmark.ts +++ /dev/null @@ -1,124 +0,0 @@ -/// -/// - -import * as fs from "fs"; -import * as path from "path"; -import * as typescript from "typescript"; -declare var ts: typeof typescript; - -var tsSourceDir = "../src"; -var tsBuildDir = "../built/local"; -var testOutputDir = "../built/benchmark"; -var sourceFiles = [ - "compiler/types.ts", - "compiler/core.ts", - "compiler/sys.ts", - "compiler/diagnosticInformationMap.generated.ts", - "compiler/scanner.ts", - "compiler/binder.ts", - "compiler/utilities.ts", - "compiler/parser.ts", - "compiler/checker.ts", - "compiler/declarationEmitter.ts", - "compiler/emitter.ts", - "compiler/program.ts", - "compiler/commandLineParser.ts", - "compiler/tsc.ts"]; - -// .ts sources for the compiler, used as a test input -var rawCompilerSources = ""; -sourceFiles.forEach(f=> { - rawCompilerSources += "\r\n" + fs.readFileSync(path.join(tsSourceDir, f)).toString(); -}); -var compilerSources = `var compilerSources = ${JSON.stringify(rawCompilerSources) };`; - -// .js code for the compiler, what we are actually testing -var rawCompilerJavaScript = fs.readFileSync(path.join(tsBuildDir, "tsc.js")).toString(); -rawCompilerJavaScript = rawCompilerJavaScript.replace("ts.executeCommandLine(ts.sys.args);", ""); - -// lib.d.ts sources -var rawLibSources = fs.readFileSync(path.join(tsBuildDir, "lib.d.ts")).toString(); -var libSources = `var libSources = ${JSON.stringify(rawLibSources) };`; - -// write test output -if (!fs.existsSync(testOutputDir)) { - fs.mkdirSync(testOutputDir); -} - -// 1. compiler ts sources, used to test -fs.writeFileSync( - path.join(testOutputDir, "compilerSources.js"), - `${ compilerSources } \r\n ${ libSources }`); - -// 2. the compiler js sources + a call the compiler -fs.writeFileSync( - path.join(testOutputDir, "benchmarktsc.js"), - `${ rawCompilerJavaScript }\r\n${ compile.toString() }\r\ncompile(compilerSources, libSources);`); - -// 3. test html file to drive the test -fs.writeFileSync( - path.join(testOutputDir, "benchmarktsc.html"), - ` - - - - Typescript 1.1 Compiler - - - -

-
End-to-End Time: N/A
- - - - - -`); - -function compile(compilerSources, librarySources) { - var program = ts.createProgram( - ["lib.d.ts", "compiler.ts"], - { - noResolve: true, - out: "compiler.js", - removeComments: true, - target: ts.ScriptTarget.ES3 - }, { - getDefaultLibFileName: () => "lib.d.ts", - getSourceFile: (filename, languageVersion) => { - var source: string; - if (filename === "lib.d.ts") source = librarySources; - else if (filename === "compiler.ts") source = compilerSources; - else console.error("Unexpected read file request: " + filename); - - return ts.createSourceFile(filename, source, languageVersion); - }, - writeFile: (filename, data, writeByteOrderMark) => { - if (filename !== "compiler.js") - console.error("Unexpected write file request: " + filename); - // console.log(data); - }, - getCurrentDirectory: () => "", - getCanonicalFileName: (filename) => filename, - useCaseSensitiveFileNames: () => false, - getNewLine: () => "\r\n" - }); - - var emitOutput = program.emit(); - - var errors = program.getSyntacticDiagnostics() - .concat(program.getSemanticDiagnostics()) - .concat(program.getGlobalDiagnostics()) - .concat(emitOutput.diagnostics); - - if (errors.length) { - console.error("Unexpected errors."); - errors.forEach(e=> console.log(`${e.code}: ${e.messageText}`)) - } -} diff --git a/scripts/ior.ts b/scripts/ior.ts deleted file mode 100644 index 374747d8439..00000000000 --- a/scripts/ior.ts +++ /dev/null @@ -1,125 +0,0 @@ -/// - -import fs = require('fs'); -import path = require('path'); - -interface IOLog { - filesRead: { - path: string; - result: { contents: string; }; - }[]; - arguments: string[]; -} - -module Commands { - export function dir(obj: IOLog) { - obj.filesRead.filter(f => f.result !== undefined).forEach(f => { - console.log(f.path); - }); - } - dir['description'] = ': displays a list of files'; - - export function find(obj: IOLog, str: string) { - obj.filesRead.filter(f => f.result !== undefined).forEach(f => { - var lines = f.result.contents.split('\n'); - var printedHeader = false; - lines.forEach(line => { - if (line.indexOf(str) >= 0) { - if (!printedHeader) { - console.log(' === ' + f.path + ' ==='); - printedHeader = true; - } - console.log(line); - } - }); - }); - } - find['description'] = ' string: finds text in files'; - - export function grab(obj: IOLog, filename: string) { - obj.filesRead.filter(f => f.result !== undefined).forEach(f => { - if (path.basename(f.path) === filename) { - fs.writeFile(filename, f.result.contents); - } - }); - } - grab['description'] = ' filename.ts: writes out the specified file to disk'; - - export function extract(obj: IOLog, outputFolder: string) { - var directorySeparator = "/"; - function directoryExists(path: string): boolean { - return fs.existsSync(path) && fs.statSync(path).isDirectory(); - } - function getDirectoryPath(path: string) { - return path.substr(0, Math.max(getRootLength(path), path.lastIndexOf(directorySeparator))); - } - function getRootLength(path: string): number { - if (path.charAt(0) === directorySeparator) { - if (path.charAt(1) !== directorySeparator) return 1; - var p1 = path.indexOf(directorySeparator, 2); - if (p1 < 0) return 2; - var p2 = path.indexOf(directorySeparator, p1 + 1); - if (p2 < 0) return p1 + 1; - return p2 + 1; - } - if (path.charAt(1) === ":") { - if (path.charAt(2) === directorySeparator) return 3; - } - return 0; - } - function ensureDirectoriesExist(directoryPath: string) { - if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) { - var parentDirectory = getDirectoryPath(directoryPath); - ensureDirectoriesExist(parentDirectory); - console.log("creating directory: " + directoryPath); - fs.mkdirSync(directoryPath); - } - } - function normalizeSlashes(path: string): string { - return path.replace(/\\/g, "/"); - } - function transalatePath(outputFolder:string, path: string): string { - return normalizeSlashes(outputFolder + directorySeparator + path.replace(":", "")); - } - function fileExists(path: string): boolean { - return fs.existsSync(path); - } - obj.filesRead.forEach(f => { - var filename = transalatePath(outputFolder, f.path); - ensureDirectoriesExist(getDirectoryPath(filename)); - console.log("writing filename: " + filename); - fs.writeFileSync(filename, f.result.contents); - }); - - console.log("Command: tsc "); - obj.arguments.forEach(a => { - if (getRootLength(a) > 0) { - console.log(transalatePath(outputFolder, a)); - } - else { - console.log(a); - } - console.log(" "); - }); - } - extract['description'] = ' outputFolder: extract all input files to '; -} - -var args = process.argv.slice(2); -if (args.length < 2) { - console.log('Usage: node ior.js path_to_file.json [command]'); - console.log('List of commands: '); - Object.keys(Commands).forEach(k => console.log(' ' + k + Commands[k]['description'])); -} else { - var cmd: Function = Commands[args[1]]; - if (cmd === undefined) { - console.log('Unknown command ' + args[1]); - } else { - fs.readFile(args[0], 'utf-8', (err, data) => { - if (err) throw err; - var json = JSON.parse(data); - cmd.apply(undefined, [json].concat(args.slice(2))); - }); - } -} - diff --git a/scripts/parallel-lint.js b/scripts/parallel-lint.js deleted file mode 100644 index 2ac84667d6f..00000000000 --- a/scripts/parallel-lint.js +++ /dev/null @@ -1,50 +0,0 @@ -var tslint = require("tslint"); -var fs = require("fs"); -var path = require("path"); - -function getLinterOptions() { - return { - formatter: "prose", - formattersDirectory: undefined, - rulesDirectory: "built/local/tslint" - }; -} -function getLinterConfiguration() { - return tslint.Configuration.loadConfigurationFromPath(path.join(__dirname, "../tslint.json")); -} - -function lintFileContents(options, configuration, path, contents) { - var ll = new tslint.Linter(options); - ll.lint(path, contents, configuration); - return ll.getResult(); -} - -function lintFileAsync(options, configuration, path, cb) { - fs.readFile(path, "utf8", function (err, contents) { - if (err) { - return cb(err); - } - var result = lintFileContents(options, configuration, path, contents); - cb(undefined, result); - }); -} - -process.on("message", function (data) { - switch (data.kind) { - case "file": - var target = data.name; - var lintOptions = getLinterOptions(); - var lintConfiguration = getLinterConfiguration(); - lintFileAsync(lintOptions, lintConfiguration, target, function (err, result) { - if (err) { - process.send({ kind: "error", error: err.toString() }); - return; - } - process.send({ kind: "result", failures: result.failureCount, output: result.output }); - }); - break; - case "close": - process.exit(0); - break; - } -}); \ No newline at end of file diff --git a/scripts/types/ambient.d.ts b/scripts/types/ambient.d.ts index 52ddca6d7c1..ecfe29f9db6 100644 --- a/scripts/types/ambient.d.ts +++ b/scripts/types/ambient.d.ts @@ -1,13 +1,5 @@ import { TaskFunction } from "gulp"; -declare module "gulp-clone" { - function Clone(): NodeJS.ReadWriteStream; - namespace Clone { - export function sink() : NodeJS.ReadWriteStream & {tap: () => NodeJS.ReadWriteStream}; - } - export = Clone; -} - declare module "gulp-insert" { export function append(text: string | Buffer): NodeJS.ReadWriteStream; export function prepend(text: string | Buffer): NodeJS.ReadWriteStream; diff --git a/scripts/types/mocha/LICENSE b/scripts/types/mocha/LICENSE deleted file mode 100644 index 21071075c24..00000000000 --- a/scripts/types/mocha/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/scripts/types/mocha/index.d.ts b/scripts/types/mocha/index.d.ts deleted file mode 100644 index 1ada77c0a49..00000000000 --- a/scripts/types/mocha/index.d.ts +++ /dev/null @@ -1,2856 +0,0 @@ -// Type definitions for mocha 5.2 -// Project: http://mochajs.org/ -// Definitions by: Kazi Manzur Rashid -// otiai10 -// jt000 -// Vadim Macagon -// Andrew Bradley -// Dmitrii Sorin -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.1 - -export = Mocha; -export as namespace Mocha; - -/** - * Mocha API - * - * @see https://mochajs.org/api/mocha - */ -declare class Mocha { - private _growl; - private _reporter; - private _ui; - - constructor(options?: Mocha.MochaOptions); - - suite: Mocha.Suite; - files: string[]; - options: Mocha.MochaInstanceOptions; - - /** - * Enable or disable bailing on the first failure. - * - * @see https://mochajs.org/api/mocha#bail - */ - bail(bail?: boolean): this; - - /** - * Add test `file`. - * - * @see https://mochajs.org/api/mocha#addFile - */ - addFile(file: string): this; - - /** - * Set reporter to one of the built-in reporters. - * - * @see https://mochajs.org/api/mocha#reporter - */ - reporter(reporter: Mocha.Reporter, reporterOptions?: any): this; - - /** - * Set reporter to the provided constructor, one of the built-in reporters, or loads a reporter - * from a module path. Defaults to `"spec"`. - * - * @see https://mochajs.org/api/mocha#reporter - */ - reporter(reporter?: string | Mocha.ReporterConstructor, reporterOptions?: any): this; - - /** - * Set test UI to one of the built-in test interfaces. - * - * @see https://mochajs.org/api/mocha#ui - */ - ui(name: Mocha.Interface): this; - - /** - * Set test UI to one of the built-in test interfaces or loads a test interface from a module - * path. Defaults to `"bdd"`. - * - * @see https://mochajs.org/api/mocha#ui - */ - ui(name?: string): this; - - /** - * Escape string and add it to grep as a RegExp. - * - * @see https://mochajs.org/api/mocha#fgrep - */ - fgrep(str: string): this; - - /** - * Add regexp to grep, if `re` is a string it is escaped. - * - * @see https://mochajs.org/api/mocha#grep - */ - grep(re: string | RegExp): this; - - /** - * Invert `.grep()` matches. - * - * @see https://mochajs.org/api/mocha#invert - */ - invert(): this; - - /** - * Ignore global leaks. - * - * @see https://mochajs.org/api/mocha#ignoreLeaks - */ - ignoreLeaks(ignore: boolean): this; - - /** - * Enable global leak checking. - * - * @see https://mochajs.org/api/mocha#checkLeaks - */ - checkLeaks(): this; - - /** - * Display long stack-trace on failing - * - * @see https://mochajs.org/api/mocha#fullTrace - */ - fullTrace(): this; - - /** - * Enable growl support. - * - * @see https://mochajs.org/api/mocha#growl - */ - growl(): this; - - /** - * Ignore `globals` array or string. - * - * @see https://mochajs.org/api/mocha#globals - */ - globals(globals: string | ReadonlyArray): this; - - /** - * Emit color output. - * - * @see https://mochajs.org/api/mocha#useColors - */ - useColors(colors: boolean): this; - - /** - * Use inline diffs rather than +/-. - * - * @see https://mochajs.org/api/mocha#useInlineDiffs - */ - useInlineDiffs(inlineDiffs: boolean): this; - - /** - * Do not show diffs at all. - * - * @see https://mochajs.org/api/mocha#hideDiff - */ - hideDiff(hideDiff: boolean): this; - - /** - * Set the timeout in milliseconds. - * - * @see https://mochajs.org/api/mocha#timeout - */ - timeout(timeout: string | number): this; - - /** - * Set the number of times to retry failed tests. - * - * @see https://mochajs.org/api/mocha#retries - */ - retries(n: number): this; - - /** - * Set slowness threshold in milliseconds. - * - * @see https://mochajs.org/api/mocha#slow - */ - slow(slow: string | number): this; - - /** - * Enable timeouts. - * - * @see https://mochajs.org/api/mocha#enableTimeouts - */ - enableTimeouts(enabled?: boolean): this; - - /** - * Makes all tests async (accepting a callback) - * - * @see https://mochajs.org/api/mocha#asyncOnly. - */ - asyncOnly(): this; - - /** - * Disable syntax highlighting (in browser). - * - * @see https://mochajs.org/api/mocha#noHighlighting - */ - noHighlighting(): this; - - /** - * Enable uncaught errors to propagate (in browser). - * - * @see https://mochajs.org/api/mocha#allowUncaught - */ - allowUncaught(): boolean; - - /** - * Delay root suite execution. - * - * @see https://mochajs.org/api/mocha#delay - */ - delay(): boolean; - - /** - * Tests marked only fail the suite - * - * @see https://mochajs.org/api/mocha#forbidOnly - */ - forbidOnly(): boolean; - - /** - * Pending tests and tests marked skip fail the suite - * - * @see https://mochajs.org/api/mocha#forbidPending - */ - forbidPending(): boolean; - - /** - * Run tests and invoke `fn()` when complete. - * - * Note that `run` relies on Node's `require` to execute - * the test interface functions and will be subject to the - * cache - if the files are already in the `require` cache, - * they will effectively be skipped. Therefore, to run tests - * multiple times or to run tests in files that are already - * in the `require` cache, make sure to clear them from the - * cache first in whichever manner best suits your needs. - * - * @see https://mochajs.org/api/mocha#run - */ - run(fn?: (failures: number) => void): Mocha.Runner; - - /** - * Load registered files. - * - * @see https://mochajs.org/api/mocha#loadFiles - */ - protected loadFiles(fn?: () => void): void; -} - -declare namespace Mocha { - namespace utils { - /** - * Compute a slug from the given `str`. - * - * @see https://mochajs.org/api/module-utils.html#.slug - */ - function slug(str: string): string; - - /** - * Strip the function definition from `str`, and re-indent for pre whitespace. - * - * @see https://mochajs.org/api/module-utils.html#.clean - */ - function clean(str: string): string; - - /** - * Highlight the given string of `js`. - */ - function highlight(js: string): string; - - /** - * Takes some variable and asks `Object.prototype.toString()` what it thinks it is. - */ - function type(value: any): string; - - /** - * Stringify `value`. Different behavior depending on type of value: - * - * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively. - * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes. - * - If `value` is an *empty* object, function, or array, returns `'{}'`, `'[Function]'`, or `'[]'` respectively. - * - If `value` has properties, call canonicalize} on it, then return result of `JSON.stringify()` - * - * @see https://mochajs.org/api/module-utils.html#.stringify - */ - function stringify(value: any): string; - - /** - * Return a new Thing that has the keys in sorted order. Recursive. - * - * If the Thing... - * - has already been seen, return string `'[Circular]'` - * - is `undefined`, return string `'[undefined]'` - * - is `null`, return value `null` - * - is some other primitive, return the value - * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method - * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again. - * - is an empty `Array`, `Object`, or `Function`, returns `'[]'`, `'{}'`, or `'[Function]'` respectively. - * - * @see https://mochajs.org/api/module-utils.html#.canonicalize - */ - function canonicalize(value: any, stack: any[], typeHint: string): any; - - /** - * Lookup file names at the given `path`. - * - * @see https://mochajs.org/api/Mocha.utils.html#.exports.lookupFiles - */ - function lookupFiles(filepath: string, extensions?: string[], recursive?: boolean): string[]; - - /** - * Generate an undefined error with a message warning the user. - * - * @see https://mochajs.org/api/module-utils.html#.undefinedError - */ - function undefinedError(): Error; - - /** - * Generate an undefined error if `err` is not defined. - * - * @see https://mochajs.org/api/module-utils.html#.getError - */ - function getError(err: Error | undefined): Error; - - /** - * When invoking this function you get a filter function that get the Error.stack as an - * input, and return a prettify output. (i.e: strip Mocha and internal node functions from - * stack trace). - * - * @see https://mochajs.org/api/module-utils.html#.stackTraceFilter - */ - function stackTraceFilter(): (stack: string) => string; - } - - namespace interfaces { - function bdd(suite: Suite): void; - function tdd(suite: Suite): void; - function qunit(suite: Suite): void; - function exports(suite: Suite): void; - } - - // #region Test interface augmentations - - interface HookFunction { - /** - * [bdd, qunit, tdd] Describe a "hook" to execute the given callback `fn`. The name of the - * function is used as the name of the hook. - * - * - _Only available when invoked via the mocha CLI._ - */ - (fn: Func): void; - - /** - * [bdd, qunit, tdd] Describe a "hook" to execute the given callback `fn`. The name of the - * function is used as the name of the hook. - * - * - _Only available when invoked via the mocha CLI._ - */ - (fn: AsyncFunc): void; - - /** - * [bdd, qunit, tdd] Describe a "hook" to execute the given `title` and callback `fn`. - * - * - _Only available when invoked via the mocha CLI._ - */ - (name: string, fn?: Func): void; - - /** - * [bdd, qunit, tdd] Describe a "hook" to execute the given `title` and callback `fn`. - * - * - _Only available when invoked via the mocha CLI._ - */ - (name: string, fn?: AsyncFunc): void; - } - - interface SuiteFunction { - /** - * [bdd, tdd] Describe a "suite" with the given `title` and callback `fn` containing - * nested suites. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string, fn: (this: Suite) => void): Suite; - - /** - * [qunit] Describe a "suite" with the given `title`. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string): Suite; - - /** - * [bdd, tdd, qunit] Indicates this suite should be executed exclusively. - * - * - _Only available when invoked via the mocha CLI._ - */ - only: ExclusiveSuiteFunction; - - /** - * [bdd, tdd] Indicates this suite should not be executed. - * - * - _Only available when invoked via the mocha CLI._ - */ - skip: PendingSuiteFunction; - } - - interface ExclusiveSuiteFunction { - /** - * [bdd, tdd] Describe a "suite" with the given `title` and callback `fn` containing - * nested suites. Indicates this suite should be executed exclusively. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string, fn: (this: Suite) => void): Suite; - - /** - * [qunit] Describe a "suite" with the given `title`. Indicates this suite should be executed - * exclusively. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string): Suite; - } - - /** - * [bdd, tdd] Describe a "suite" with the given `title` and callback `fn` containing - * nested suites. Indicates this suite should not be executed. - * - * - _Only available when invoked via the mocha CLI._ - * - * @returns [bdd] `Suite` - * @returns [tdd] `void` - */ - type PendingSuiteFunction = (title: string, fn: (this: Suite) => void) => Suite | void; - - interface TestFunction { - /** - * Describe a specification or test-case with the given callback `fn` acting as a thunk. - * The name of the function is used as the name of the test. - * - * - _Only available when invoked via the mocha CLI._ - */ - (fn: Func): Test; - - /** - * Describe a specification or test-case with the given callback `fn` acting as a thunk. - * The name of the function is used as the name of the test. - * - * - _Only available when invoked via the mocha CLI._ - */ - (fn: AsyncFunc): Test; - - /** - * Describe a specification or test-case with the given `title` and callback `fn` acting - * as a thunk. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string, fn?: Func): Test; - - /** - * Describe a specification or test-case with the given `title` and callback `fn` acting - * as a thunk. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string, fn?: AsyncFunc): Test; - - /** - * Indicates this test should be executed exclusively. - * - * - _Only available when invoked via the mocha CLI._ - */ - only: ExclusiveTestFunction; - - /** - * Indicates this test should not be executed. - * - * - _Only available when invoked via the mocha CLI._ - */ - skip: PendingTestFunction; - - /** - * Number of attempts to retry. - * - * - _Only available when invoked via the mocha CLI._ - */ - retries(n: number): void; - } - - interface ExclusiveTestFunction { - /** - * [bdd, tdd, qunit] Describe a specification or test-case with the given callback `fn` - * acting as a thunk. The name of the function is used as the name of the test. Indicates - * this test should be executed exclusively. - * - * - _Only available when invoked via the mocha CLI._ - */ - (fn: Func): Test; - - /** - * [bdd, tdd, qunit] Describe a specification or test-case with the given callback `fn` - * acting as a thunk. The name of the function is used as the name of the test. Indicates - * this test should be executed exclusively. - * - * - _Only available when invoked via the mocha CLI._ - */ - (fn: AsyncFunc): Test; - - /** - * [bdd, tdd, qunit] Describe a specification or test-case with the given `title` and - * callback `fn` acting as a thunk. Indicates this test should be executed exclusively. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string, fn?: Func): Test; - - /** - * [bdd, tdd, qunit] Describe a specification or test-case with the given `title` and - * callback `fn` acting as a thunk. Indicates this test should be executed exclusively. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string, fn?: AsyncFunc): Test; - } - - interface PendingTestFunction { - /** - * [bdd, tdd, qunit] Describe a specification or test-case with the given callback `fn` - * acting as a thunk. The name of the function is used as the name of the test. Indicates - * this test should not be executed. - * - * - _Only available when invoked via the mocha CLI._ - */ - (fn: Func): Test; - - /** - * [bdd, tdd, qunit] Describe a specification or test-case with the given callback `fn` - * acting as a thunk. The name of the function is used as the name of the test. Indicates - * this test should not be executed. - * - * - _Only available when invoked via the mocha CLI._ - */ - (fn: AsyncFunc): Test; - - /** - * [bdd, tdd, qunit] Describe a specification or test-case with the given `title` and - * callback `fn` acting as a thunk. Indicates this test should not be executed. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string, fn?: Func): Test; - - /** - * [bdd, tdd, qunit] Describe a specification or test-case with the given `title` and - * callback `fn` acting as a thunk. Indicates this test should not be executed. - * - * - _Only available when invoked via the mocha CLI._ - */ - (title: string, fn?: AsyncFunc): Test; - } - - /** - * Execute after each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#afterEach - */ - let afterEach: HookFunction; - - /** - * Execute after running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#after - */ - let after: HookFunction; - - /** - * Execute before each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#beforeEach - */ - let beforeEach: HookFunction; - - /** - * Execute before running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#before - */ - let before: HookFunction; - - /** - * Describe a "suite" containing nested suites and tests. - * - * - _Only available when invoked via the mocha CLI._ - */ - let describe: SuiteFunction; - - /** - * Describes a test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - let it: TestFunction; - - /** - * Describes a pending test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - let xit: PendingTestFunction; - - /** - * Execute before each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#beforeEach - */ - let setup: HookFunction; - - /** - * Execute before running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#before - */ - let suiteSetup: HookFunction; - - /** - * Execute after running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#after - */ - let suiteTeardown: HookFunction; - - /** - * Describe a "suite" containing nested suites and tests. - * - * - _Only available when invoked via the mocha CLI._ - */ - let suite: SuiteFunction; - - /** - * Execute after each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#afterEach - */ - let teardown: HookFunction; - - /** - * Describes a test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - let test: TestFunction; - - /** - * Triggers root suite execution. - * - * - _Only available if flag --delay is passed into Mocha._ - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#runWithSuite - */ - function run(): void; - - // #endregion Test interface augmentations - - namespace reporters { - /** - * Initialize a new `Base` reporter. - * - * All other reporters generally inherit from this reporter, providing stats such as test duration, - * number of tests passed / failed, etc. - * - * @see https://mochajs.org/api/Mocha.reporters.Base.html - */ - class Base { - constructor(runner: Runner, options?: MochaOptions); - /** @deprecated Use the overload that accepts `Mocha.Runner` instead. */ - constructor(runner: IRunner, options?: MochaOptions); - - /** - * Test run statistics - */ - stats: Stats; - - /** - * Test failures - */ - failures: Test[]; - - /** - * The configured runner - */ - runner: Runner; - - /** - * Output common epilogue used by many of the bundled reporters. - * - * @see https://mochajs.org/api/Mocha.reporters.Base.html#.Base#epilogue - */ - epilogue(): void; - - done?(failures: number, fn?: (failures: number) => void): void; - } - - namespace Base { - /** - * Enables coloring by default - * - * @see https://mochajs.org/api/module-base#.useColors - */ - let useColors: boolean; - - /** - * Inline diffs instead of +/- - * - * @see https://mochajs.org/api/module-base#.inlineDiffs - */ - let inlineDiffs: boolean; - - /** - * Default color map - * - * @see https://mochajs.org/api/module-base#.colors - */ - const colors: ColorMap; - - /** - * Default color map - * - * @see https://mochajs.org/api/module-base#.colors - */ - interface ColorMap { - // added by Base - pass: number; - fail: number; - "bright pass": number; - "bright fail": number; - "bright yellow": number; - pending: number; - suite: number; - "error title": number; - "error message": number; - "error stack": number; - checkmark: number; - fast: number; - medium: number; - slow: number; - green: number; - light: number; - "diff gutter": number; - "diff added": number; - "diff removed": number; - - // added by Progress - progress: number; - - // added by Landing - plane: number; - "plane crash": number; - runway: number; - - [key: string]: number; - } - - /** - * Default symbol map - * - * @see https://mochajs.org/api/module-base#.symbols - */ - const symbols: SymbolMap; - - /** - * Default symbol map - * - * @see https://mochajs.org/api/module-base#.symbols - */ - interface SymbolMap { - ok: string; - err: string; - dot: string; - comma: string; - bang: string; - [key: string]: string; - } - - /** - * Color `str` with the given `type` (from `colors`) - * - * @see https://mochajs.org/api/module-base#.color - */ - function color(type: string, str: string): string; - - /** - * Expose terminal window size - * - * @see https://mochajs.org/api/module-base#.window - */ - const window: { - width: number; - }; - - /** - * ANSI TTY control sequences common among reporters. - * - * @see https://mochajs.org/api/module-base#.cursor - */ - namespace cursor { - /** - * Hides the cursor - */ - function hide(): void; - - /** - * Shows the cursor - */ - function show(): void; - - /** - * Deletes the current line - */ - function deleteLine(): void; - - /** - * Moves to the beginning of the line - */ - function beginningOfLine(): void; - - /** - * Clears the line and moves to the beginning of the line. - */ - function CR(): void; - } - - /** - * Returns a diff between two strings with colored ANSI output. - * - * @see https://mochajs.org/api/module-base#.generateDiff - */ - function generateDiff(actual: string, expected: string): string; - - /** - * Output the given `failures` as a list. - * - * @see https://mochajs.org/api/Mocha.reporters.Base.html#.exports.list1 - */ - function list(failures: Test[]): void; - } - - /** - * Initialize a new `Dot` matrix test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.Dot.html - */ - class Dot extends Base { - } - - /** - * Initialize a new `Doc` reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.Doc.html - */ - class Doc extends Base { - } - - /** - * Initialize a new `TAP` test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.TAP.html - */ - class TAP extends Base { - } - - /** - * Initialize a new `JSON` reporter - * - * @see https://mochajs.org/api/Mocha.reporters.JSON.html - */ - class JSON extends Base { - } - - /** - * Initialize a new `HTML` reporter. - * - * - _This reporter cannot be used on the console._ - * - * @see https://mochajs.org/api/Mocha.reporters.HTML.html - */ - class HTML extends Base { - /** - * Provide suite URL. - * - * @see https://mochajs.org/api/Mocha.reporters.HTML.html#suiteURL - */ - suiteURL(suite: Suite): string; - - /** - * Provide test URL. - * - * @see https://mochajs.org/api/Mocha.reporters.HTML.html#testURL - */ - testURL(test: Test): string; - - /** - * Adds code toggle functionality for the provided test's list element. - * - * @see https://mochajs.org/api/Mocha.reporters.HTML.html#addCodeToggle - */ - addCodeToggle(el: HTMLLIElement, contents: string): void; - } - - /** - * Initialize a new `List` test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.List.html - */ - class List extends Base { - } - - /** - * Initialize a new `Min` minimal test reporter (best used with --watch). - * - * @see https://mochajs.org/api/Mocha.reporters.Min.html - */ - class Min extends Base { - } - - /** - * Initialize a new `Spec` test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.Spec.html - */ - class Spec extends Base { - } - - /** - * Initialize a new `NyanCat` test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.Nyan.html - */ - class Nyan extends Base { - private colorIndex; - private numberOfLines; - private rainbowColors; - private scoreboardWidth; - private tick; - private trajectories; - private trajectoryWidthMax; - private draw; - private drawScoreboard; - private appendRainbow; - private drawRainbow; - private drawNyanCat; - private face; - private cursorUp; - private cursorDown; - private generateColors; - private rainbowify; - } - - /** - * Initialize a new `XUnit` test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.XUnit.html - */ - class XUnit extends Base { - constructor(runner: Runner, options?: XUnit.MochaOptions); - /** @deprecated Use the overload that accepts `Mocha.Runner` instead. */ - constructor(runner: IRunner, options?: XUnit.MochaOptions); - - /** - * Override done to close the stream (if it's a file). - * - * @see https://mochajs.org/api/Mocha.reporters.XUnit.html#done - */ - done(failures: number, fn: (failures: number) => void): void; - - /** - * Write out the given line. - * - * @see https://mochajs.org/api/Mocha.reporters.XUnit.html#write - */ - write(line: string): void; - - /** - * Output tag for the given `test.` - * - * @see https://mochajs.org/api/Mocha.reporters.XUnit.html#test - */ - test(test: Test): void; - } - - namespace XUnit { - interface MochaOptions extends Mocha.MochaOptions { - reporterOptions?: ReporterOptions; - } - - interface ReporterOptions { - output?: string; - suiteName?: string; - } - } - - /** - * Initialize a new `Markdown` test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.Markdown.html - */ - class Markdown extends Base { - } - - /** - * Initialize a new `Progress` bar test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.Progress.html - */ - class Progress extends Base { - constructor(runner: Runner, options?: Progress.MochaOptions); - /** @deprecated Use the overload that accepts `Mocha.Runner` instead. */ - constructor(runner: IRunner, options?: Progress.MochaOptions); - } - - namespace Progress { - interface MochaOptions extends Mocha.MochaOptions { - reporterOptions?: ReporterOptions; - } - - interface ReporterOptions { - open?: string; - complete?: string; - incomplete?: string; - close?: string; - verbose?: boolean; - } - } - - /** - * Initialize a new `Landing` reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.Landing.html - */ - class Landing extends Base { - } - - /** - * Initialize a new `JSONStream` test reporter. - * - * @see https://mochajs.org/api/Mocha.reporters.JSONStream.html - */ - class JSONStream extends Base { - } - - // value-only aliases - const base: typeof Base; - const dot: typeof Dot; - const doc: typeof Doc; - const tap: typeof TAP; - const json: typeof JSON; - const html: typeof HTML; - const list: typeof List; - const spec: typeof Spec; - const nyan: typeof Nyan; - const xunit: typeof XUnit; - const markdown: typeof Markdown; - const progress: typeof Progress; - const landing: typeof Landing; - // NOTE: not possible to type this correctly: - // const "json-stream": typeof JSONStream; - } - - /** - * Initialize a new `Runnable` with the given `title` and callback `fn`. - * - * @see https://mochajs.org/api/Runnable.html - */ - class Runnable { - private _slow; - private _enableTimeouts; - private _retries; - private _currentRetry; - private _timeout; - private _timeoutError; - - constructor(title: string, fn?: Func | AsyncFunc); - - title: string; - fn: Func | AsyncFunc | undefined; - body: string; - async: boolean; - sync: boolean; - timedOut: boolean; - pending: boolean; - duration?: number; - parent?: Suite; - state?: "failed" | "passed"; - timer?: any; - ctx?: Context; - callback?: Done; - allowUncaught?: boolean; - file?: string; - - /** - * Get test timeout. - * - * @see https://mochajs.org/api/Runnable.html#timeout - */ - timeout(): number; - - /** - * Set test timeout. - * - * @see https://mochajs.org/api/Runnable.html#timeout - */ - timeout(ms: string | number): this; - - /** - * Get test slowness threshold. - * - * @see https://mochajs.org/api/Runnable.html#slow - */ - slow(): number; - - /** - * Set test slowness threshold. - * - * @see https://mochajs.org/api/Runnable.html#slow - */ - slow(ms: string | number): this; - - /** - * Get whether timeouts are enabled. - * - * @see https://mochajs.org/api/Runnable.html#enableTimeouts - */ - enableTimeouts(): boolean; - - /** - * Set whether timeouts are enabled. - * - * @see https://mochajs.org/api/Runnable.html#enableTimeouts - */ - enableTimeouts(enabled: boolean): this; - - /** - * Halt and mark as pending. - */ - skip(): never; - - /** - * Check if this runnable or its parent suite is marked as pending. - * - * @see https://mochajs.org/api/Runnable.html#isPending - */ - isPending(): boolean; - - /** - * Return `true` if this Runnable has failed. - */ - isFailed(): boolean; - - /** - * Return `true` if this Runnable has passed. - */ - isPassed(): boolean; - - /** - * Set or get number of retries. - * - * @see https://mochajs.org/api/Runnable.html#retries - */ - retries(): number; - - /** - * Set or get number of retries. - * - * @see https://mochajs.org/api/Runnable.html#retries - */ - retries(n: number): void; - - /** - * Set or get current retry - * - * @see https://mochajs.org/api/Runnable.html#currentRetry - */ - protected currentRetry(): number; - - /** - * Set or get current retry - * - * @see https://mochajs.org/api/Runnable.html#currentRetry - */ - protected currentRetry(n: number): void; - - /** - * Return the full title generated by recursively concatenating the parent's full title. - */ - fullTitle(): string; - - /** - * Return the title path generated by concatenating the parent's title path with the title. - */ - titlePath(): string[]; - - /** - * Clear the timeout. - * - * @see https://mochajs.org/api/Runnable.html#clearTimeout - */ - clearTimeout(): void; - - /** - * Inspect the runnable void of private properties. - * - * @see https://mochajs.org/api/Runnable.html#inspect - */ - inspect(): string; - - /** - * Reset the timeout. - * - * @see https://mochajs.org/api/Runnable.html#resetTimeout - */ - resetTimeout(): void; - - /** - * Get a list of whitelisted globals for this test run. - * - * @see https://mochajs.org/api/Runnable.html#globals - */ - globals(): string[]; - - /** - * Set a list of whitelisted globals for this test run. - * - * @see https://mochajs.org/api/Runnable.html#globals - */ - globals(globals: ReadonlyArray): void; - - /** - * Run the test and invoke `fn(err)`. - * - * @see https://mochajs.org/api/Runnable.html#run - */ - run(fn: Done): void; - } - - // #region Runnable "error" event - interface Runnable extends NodeJS.EventEmitter { - on(event: "error", listener: (error: any) => void): this; - once(event: "error", listener: (error: any) => void): this; - addListener(event: "error", listener: (error: any) => void): this; - removeListener(event: "error", listener: (error: any) => void): this; - prependListener(event: "error", listener: (error: any) => void): this; - prependOnceListener(event: "error", listener: (error: any) => void): this; - emit(name: "error", error: any): boolean; - } - // #endregion Runnable "error" event - // #region Runnable untyped events - interface Runnable extends NodeJS.EventEmitter { - on(event: string, listener: (...args: any[]) => void): this; - once(event: string, listener: (...args: any[]) => void): this; - addListener(event: string, listener: (...args: any[]) => void): this; - removeListener(event: string, listener: (...args: any[]) => void): this; - prependListener(event: string, listener: (...args: any[]) => void): this; - prependOnceListener(event: string, listener: (...args: any[]) => void): this; - emit(name: string, ...args: any[]): boolean; - } - // #endregion Runnable untyped events - - /** - * Test context - * - * @see https://mochajs.org/api/module-Context.html#~Context - */ - class Context { - private _runnable; - - test?: Runnable; - currentTest?: Test; - - /** - * Get the context `Runnable`. - */ - runnable(): Runnable; - - /** - * Set the context `Runnable`. - */ - runnable(runnable: Runnable): this; - /** @deprecated Use the overload that accepts `Mocha.Runnable` instead. */ - runnable(runnable: IRunnable): this; - - /** - * Get test timeout. - */ - timeout(): number; - - /** - * Set test timeout. - */ - timeout(ms: string | number): this; - - /** - * Get whether timeouts are enabled. - */ - enableTimeouts(): boolean; - - /** - * Set whether timeouts are enabled. - */ - enableTimeouts(enabled: boolean): this; - - /** - * Get test slowness threshold. - */ - slow(): number; - - /** - * Set test slowness threshold. - */ - slow(ms: string | number): this; - - /** - * Mark a test as skipped. - */ - skip(): never; - - /** - * Get the number of allowed retries on failed tests. - */ - retries(): number; - - /** - * Set the number of allowed retries on failed tests. - */ - retries(n: number): this; - - [key: string]: any; - } - - /** - * Initialize a `Runner` for the given `suite`. - * - * @see https://mochajs.org/api/Mocha.Runner.html - */ - class Runner { - private _globals; - private _abort; - private _delay; - private _defaultGrep; - private next; - private hookErr; - private prevGlobalsLength; - private nextSuite; - - constructor(suite: Suite, delay: boolean); - - /** @deprecated Use the overload that accepts `Mocha.Suite` instead. */ - constructor(suite: ISuite, delay: boolean); - - suite: Suite; - started: boolean; - total: number; - failures: number; - asyncOnly?: boolean; - allowUncaught?: boolean; - fullStackTrace?: boolean; - forbidOnly?: boolean; - forbidPending?: boolean; - ignoreLeaks?: boolean; - test?: Test; - currentRunnable?: Runnable; - stats?: Stats; // added by reporters - - /** - * Run tests with full titles matching `re`. Updates runner.total - * with number of tests matched. - * - * @see https://mochajs.org/api/Mocha.Runner.html#.Runner#grep - */ - grep(re: RegExp, invert: boolean): this; - - /** - * Returns the number of tests matching the grep search for the - * given suite. - * - * @see https://mochajs.org/api/Mocha.Runner.html#.Runner#grepTotal - */ - grepTotal(suite: Suite): number; - - /** @deprecated Use the overload that accepts `Mocha.Suite` instead. */ - grepTotal(suite: ISuite): number; - - /** - * Gets the allowed globals. - * - * @see https://mochajs.org/api/Mocha.Runner.html#.Runner#globals - */ - globals(): string[]; - - /** - * Allow the given `arr` of globals. - * - * @see https://mochajs.org/api/Mocha.Runner.html#.Runner#globals - */ - globals(arr: ReadonlyArray): this; - - /** - * Run the root suite and invoke `fn(failures)` on completion. - * - * @see https://mochajs.org/api/Mocha.Runner.html#.Runner#run - */ - run(fn?: (failures: number) => void): this; - - /** - * Cleanly abort execution. - * - * @see https://mochajs.org/api/Mocha.Runner.html#.Runner#abort - */ - abort(): this; - - /** - * Handle uncaught exceptions. - * - * @see https://mochajs.org/api/Mocha.Runner.html#uncaught - */ - uncaught(err: any): void; - - /** - * Wrapper for setImmediate, process.nextTick, or browser polyfill. - */ - protected static immediately(callback: Function): void; - - /** - * Return a list of global properties. - * - * @see https://mochajs.org/api/Mocha.Runner.html#globalProps - */ - protected globalProps(): string[]; - - /** - * Check for global variable leaks. - * - * @see https://mochajs.org/api/Mocha.Runner.html#checkGlobals - */ - protected checkGlobals(test: Test): void; - - /** - * Fail the given `test`. - * - * @see https://mochajs.org/api/Mocha.Runner.html#fail - */ - protected fail(test: Test, err: any): void; - - /** - * Fail the given `hook` with `err`. - * - * Hook failures work in the following pattern: - * - If bail, then exit - * - Failed `before` hook skips all tests in a suite and subsuites, - * but jumps to corresponding `after` hook - * - Failed `before each` hook skips remaining tests in a - * suite and jumps to corresponding `after each` hook, - * which is run only once - * - Failed `after` hook does not alter - * execution order - * - Failed `after each` hook skips remaining tests in a - * suite and subsuites, but executes other `after each` - * hooks - * - * @see https://mochajs.org/api/Mocha.Runner.html#failHook - */ - protected failHook(hook: Hook, err: any): void; - - /** - * Run hook `name` callbacks and then invoke `fn()`. - * - * @see https://mochajs.org/api/Mocha.Runner.html#hook - */ - protected hook(name: string, fn: () => void): void; - - /** - * Run hook `name` for the given array of `suites` - * in order, and callback `fn(err, errSuite)`. - * - * @see https://mochajs.org/api/Mocha.Runner.html#hooks - */ - protected hooks(name: string, suites: Suite[], fn: (err?: any, errSuite?: Suite) => void): void; - - /** - * Run hooks from the top level down. - * - * @see https://mochajs.org/api/Mocha.Runner.html#hookUp - */ - protected hookUp(name: string, fn: (err?: any, errSuite?: Suite) => void): void; - - /** - * Run hooks from the bottom up. - * - * @see https://mochajs.org/api/Mocha.Runner.html#hookDown - */ - protected hookDown(name: string, fn: (err?: any, errSuite?: Suite) => void): void; - - /** - * Return an array of parent Suites from closest to furthest. - * - * @see https://mochajs.org/api/Mocha.Runner.html#parents - */ - protected parents(): Suite[]; - - /** - * Run the current test and callback `fn(err)`. - * - * @see https://mochajs.org/api/Mocha.Runner.html#runTest - */ - protected runTest(fn: Done): any; - - /** - * Run tests in the given `suite` and invoke the callback `fn()` when complete. - * - * @see https://mochajs.org/api/Mocha.Runner.html#runTests - */ - protected runTests(suite: Suite, fn: (errSuite?: Suite) => void): void; - - /** - * Run the given `suite` and invoke the callback `fn()` when complete. - * - * @see https://mochajs.org/api/Mocha.Runner.html#runSuite - */ - protected runSuite(suite: Suite, fn: (errSuite?: Suite) => void): void; - } - - // #region Runner "waiting" event - interface Runner { - on(event: "waiting", listener: (rootSuite: Suite) => void): this; - once(event: "waiting", listener: (rootSuite: Suite) => void): this; - addListener(event: "waiting", listener: (rootSuite: Suite) => void): this; - removeListener(event: "waiting", listener: (rootSuite: Suite) => void): this; - prependListener(event: "waiting", listener: (rootSuite: Suite) => void): this; - prependOnceListener(event: "waiting", listener: (rootSuite: Suite) => void): this; - emit(name: "waiting", rootSuite: Suite): boolean; - } - // #endregion Runner "waiting" event - // #region Runner "start" event - interface Runner extends NodeJS.EventEmitter { - on(event: "start", listener: () => void): this; - once(event: "start", listener: () => void): this; - addListener(event: "start", listener: () => void): this; - removeListener(event: "start", listener: () => void): this; - prependListener(event: "start", listener: () => void): this; - prependOnceListener(event: "start", listener: () => void): this; - emit(name: "start"): boolean; - } - // #endregion Runner "start" event - // #region Runner "end" event - interface Runner extends NodeJS.EventEmitter { - on(event: "end", listener: () => void): this; - once(event: "end", listener: () => void): this; - addListener(event: "end", listener: () => void): this; - removeListener(event: "end", listener: () => void): this; - prependListener(event: "end", listener: () => void): this; - prependOnceListener(event: "end", listener: () => void): this; - emit(name: "end"): boolean; - } - // #endregion Runner "end" event - // #region Runner "suite" event - interface Runner extends NodeJS.EventEmitter { - on(event: "suite", listener: (suite: Suite) => void): this; - once(event: "suite", listener: (suite: Suite) => void): this; - addListener(event: "suite", listener: (suite: Suite) => void): this; - removeListener(event: "suite", listener: (suite: Suite) => void): this; - prependListener(event: "suite", listener: (suite: Suite) => void): this; - prependOnceListener(event: "suite", listener: (suite: Suite) => void): this; - emit(name: "suite", suite: Suite): boolean; - } - // #endregion Runner "suite" event - // #region Runner "suite end" event - interface Runner extends NodeJS.EventEmitter { - on(event: "suite end", listener: (suite: Suite) => void): this; - once(event: "suite end", listener: (suite: Suite) => void): this; - addListener(event: "suite end", listener: (suite: Suite) => void): this; - removeListener(event: "suite end", listener: (suite: Suite) => void): this; - prependListener(event: "suite end", listener: (suite: Suite) => void): this; - prependOnceListener(event: "suite end", listener: (suite: Suite) => void): this; - emit(name: "suite end", suite: Suite): boolean; - } - // #endregion Runner "suite end" event - // #region Runner "test" event - interface Runner extends NodeJS.EventEmitter { - on(event: "test", listener: (test: Test) => void): this; - once(event: "test", listener: (test: Test) => void): this; - addListener(event: "test", listener: (test: Test) => void): this; - removeListener(event: "test", listener: (test: Test) => void): this; - prependListener(event: "test", listener: (test: Test) => void): this; - prependOnceListener(event: "test", listener: (test: Test) => void): this; - emit(name: "test", test: Test): boolean; - } - // #endregion Runner "test" event - // #region Runner "test end" event - interface Runner extends NodeJS.EventEmitter { - on(event: "test end", listener: (test: Test) => void): this; - once(event: "test end", listener: (test: Test) => void): this; - addListener(event: "test end", listener: (test: Test) => void): this; - removeListener(event: "test end", listener: (test: Test) => void): this; - prependListener(event: "test end", listener: (test: Test) => void): this; - prependOnceListener(event: "test end", listener: (test: Test) => void): this; - emit(name: "test end", test: Test): boolean; - } - // #endregion Runner "test end" event - // #region Runner "hook" event - interface Runner extends NodeJS.EventEmitter { - on(event: "hook", listener: (hook: Hook) => void): this; - once(event: "hook", listener: (hook: Hook) => void): this; - addListener(event: "hook", listener: (hook: Hook) => void): this; - removeListener(event: "hook", listener: (hook: Hook) => void): this; - prependListener(event: "hook", listener: (hook: Hook) => void): this; - prependOnceListener(event: "hook", listener: (hook: Hook) => void): this; - emit(name: "hook", hook: Hook): boolean; - } - // #endregion Runner "hook" event - // #region Runner "hook end" event - interface Runner extends NodeJS.EventEmitter { - on(event: "hook end", listener: (hook: Hook) => void): this; - once(event: "hook end", listener: (hook: Hook) => void): this; - addListener(event: "hook end", listener: (hook: Hook) => void): this; - removeListener(event: "hook end", listener: (hook: Hook) => void): this; - prependListener(event: "hook end", listener: (hook: Hook) => void): this; - prependOnceListener(event: "hook end", listener: (hook: Hook) => void): this; - emit(name: "hook end", hook: Hook): boolean; - } - // #endregion Runner "hook end" event - // #region Runner "pass" event - interface Runner extends NodeJS.EventEmitter { - on(event: "pass", listener: (test: Test) => void): this; - once(event: "pass", listener: (test: Test) => void): this; - addListener(event: "pass", listener: (test: Test) => void): this; - removeListener(event: "pass", listener: (test: Test) => void): this; - prependListener(event: "pass", listener: (test: Test) => void): this; - prependOnceListener(event: "pass", listener: (test: Test) => void): this; - emit(name: "pass", test: Test): boolean; - } - // #endregion Runner "pass" event - // #region Runner "fail" event - interface Runner extends NodeJS.EventEmitter { - on(event: "fail", listener: (test: Test, err: any) => void): this; - once(event: "fail", listener: (test: Test, err: any) => void): this; - addListener(event: "fail", listener: (test: Test, err: any) => void): this; - removeListener(event: "fail", listener: (test: Test, err: any) => void): this; - prependListener(event: "fail", listener: (test: Test, err: any) => void): this; - prependOnceListener(event: "fail", listener: (test: Test, err: any) => void): this; - emit(name: "fail", test: Test, err: any): boolean; - } - // #endregion Runner "fail" event - // #region Runner "pending" event - interface Runner extends NodeJS.EventEmitter { - on(event: "pending", listener: (test: Test) => void): this; - once(event: "pending", listener: (test: Test) => void): this; - addListener(event: "pending", listener: (test: Test) => void): this; - removeListener(event: "pending", listener: (test: Test) => void): this; - prependListener(event: "pending", listener: (test: Test) => void): this; - prependOnceListener(event: "pending", listener: (test: Test) => void): this; - emit(name: "pending", test: Test): boolean; - } - // #endregion Runner "pending" event - // #region Runner untyped events - interface Runner extends NodeJS.EventEmitter { - on(event: string, listener: (...args: any[]) => void): this; - once(event: string, listener: (...args: any[]) => void): this; - addListener(event: string, listener: (...args: any[]) => void): this; - removeListener(event: string, listener: (...args: any[]) => void): this; - prependListener(event: string, listener: (...args: any[]) => void): this; - prependOnceListener(event: string, listener: (...args: any[]) => void): this; - emit(name: string, ...args: any[]): boolean; - } - // #endregion Runner untyped events - - /** - * Initialize a new `Suite` with the given `title` and `ctx`. - * - * @see https://mochajs.org/api/Mocha.Suite.html - */ - class Suite { - private _beforeEach; - private _beforeAll; - private _afterEach; - private _afterAll; - private _timeout; - private _enableTimeouts; - private _slow; - private _bail; - private _retries; - private _onlyTests; - private _onlySuites; - - constructor(title: string, parentContext?: Context); - /** @deprecated Use the overload that accepts `Mocha.Context` instead. */ - constructor(title: string, parentContext?: IContext); - - ctx: Context; - suites: Suite[]; - tests: Test[]; - pending: boolean; - file?: string; - root: boolean; - delayed: boolean; - parent: Suite | undefined; - title: string; - - /** - * Create a new `Suite` with the given `title` and parent `Suite`. When a suite - * with the same title is already present, that suite is returned to provide - * nicer reporter and more flexible meta-testing. - * - * @see https://mochajs.org/api/mocha#.exports.create - */ - static create(parent: Suite, title: string): Suite; - /** @deprecated Use the overload that accepts `Mocha.Suite` instead. */ - static create(parent: ISuite, title: string): Suite; - - /** - * Return a clone of this `Suite`. - * - * @see https://mochajs.org/api/Mocha.Suite.html#clone - */ - clone(): Suite; - - /** - * Get timeout `ms`. - * - * @see https://mochajs.org/api/Mocha.Suite.html#timeout - */ - timeout(): number; - - /** - * Set timeout `ms` or short-hand such as "2s". - * - * @see https://mochajs.org/api/Mocha.Suite.html#timeout - */ - timeout(ms: string | number): this; - - /** - * Get number of times to retry a failed test. - * - * @see https://mochajs.org/api/Mocha.Suite.html#retries - */ - retries(): number; - - /** - * Set number of times to retry a failed test. - * - * @see https://mochajs.org/api/Mocha.Suite.html#retries - */ - retries(n: string | number): this; - - /** - * Get whether timeouts are enabled. - * - * @see https://mochajs.org/api/Mocha.Suite.html#enableTimeouts - */ - enableTimeouts(): boolean; - - /** - * Set whether timeouts are `enabled`. - * - * @see https://mochajs.org/api/Mocha.Suite.html#enableTimeouts - */ - enableTimeouts(enabled: boolean): this; - - /** - * Get slow `ms`. - * - * @see https://mochajs.org/api/Mocha.Suite.html#slow - */ - slow(): number; - - /** - * Set slow `ms` or short-hand such as "2s". - * - * @see https://mochajs.org/api/Mocha.Suite.html#slow - */ - slow(ms: string | number): this; - - /** - * Get whether to bail after first error. - * - * @see https://mochajs.org/api/Mocha.Suite.html#bail - */ - bail(): boolean; - - /** - * Set whether to bail after first error. - * - * @see https://mochajs.org/api/Mocha.Suite.html#bail - */ - bail(bail: boolean): this; - - /** - * Check if this suite or its parent suite is marked as pending. - * - * @see https://mochajs.org/api/Mocha.Suite.html#isPending - */ - isPending(): boolean; - - /** - * Run `fn(test[, done])` before running tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#beforeAll - */ - beforeAll(fn?: Func): this; - - /** - * Run `fn(test[, done])` before running tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#beforeAll - */ - beforeAll(fn?: AsyncFunc): this; - - /** - * Run `fn(test[, done])` before running tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#beforeAll - */ - beforeAll(title: string, fn?: Func): this; - - /** - * Run `fn(test[, done])` before running tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#beforeAll - */ - beforeAll(title: string, fn?: AsyncFunc): this; - - /** - * Run `fn(test[, done])` after running tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#afterAll - */ - afterAll(fn?: Func): this; - - /** - * Run `fn(test[, done])` after running tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#afterAll - */ - afterAll(fn?: AsyncFunc): this; - - /** - * Run `fn(test[, done])` after running tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#afterAll - */ - afterAll(title: string, fn?: Func): this; - - /** - * Run `fn(test[, done])` after running tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#afterAll - */ - afterAll(title: string, fn?: AsyncFunc): this; - - /** - * Run `fn(test[, done])` before each test case. - * - * @see https://mochajs.org/api/Mocha.Suite.html#beforeEach - */ - beforeEach(fn?: Func): this; - - /** - * Run `fn(test[, done])` before each test case. - * - * @see https://mochajs.org/api/Mocha.Suite.html#beforeEach - */ - beforeEach(fn?: AsyncFunc): this; - - /** - * Run `fn(test[, done])` before each test case. - * - * @see https://mochajs.org/api/Mocha.Suite.html#beforeEach - */ - beforeEach(title: string, fn?: Func): this; - - /** - * Run `fn(test[, done])` before each test case. - * - * @see https://mochajs.org/api/Mocha.Suite.html#beforeEach - */ - beforeEach(title: string, fn?: AsyncFunc): this; - - /** - * Run `fn(test[, done])` after each test case. - * - * @see https://mochajs.org/api/Mocha.Suite.html#afterEach - */ - afterEach(fn?: Func): this; - - /** - * Run `fn(test[, done])` after each test case. - * - * @see https://mochajs.org/api/Mocha.Suite.html#afterEach - */ - afterEach(fn?: AsyncFunc): this; - - /** - * Run `fn(test[, done])` after each test case. - * - * @see https://mochajs.org/api/Mocha.Suite.html#afterEach - */ - afterEach(title: string, fn?: Func): this; - - /** - * Run `fn(test[, done])` after each test case. - * - * @see https://mochajs.org/api/Mocha.Suite.html#afterEach - */ - afterEach(title: string, fn?: AsyncFunc): this; - - /** - * Add a test `suite`. - * - * @see https://mochajs.org/api/Mocha.Suite.html#addSuite - */ - addSuite(suite: Suite): this; - /** @deprecated Use the overload that accepts `Mocha.ISuite` instead. */ - addSuite(suite: ISuite): this; - - /** - * Add a `test` to this suite. - * - * @see https://mochajs.org/api/Mocha.Suite.html#addTest - */ - addTest(test: Test): this; - /** @deprecated Use the overload that accepts `Mocha.ITest` instead. */ - addTest(test: ITest): this; - - /** - * Return the full title generated by recursively concatenating the parent's - * full title. - * - * @see https://mochajs.org/api/Mocha.Suite.html#.Suite#fullTitle - */ - fullTitle(): string; - - /** - * Return the title path generated by recursively concatenating the parent's - * title path. - * - * @see https://mochajs.org/api/Mocha.Suite.html#.Suite#titlePath - */ - titlePath(): string[]; - - /** - * Return the total number of tests. - * - * @see https://mochajs.org/api/Mocha.Suite.html#.Suite#total - */ - total(): number; - - /** - * Iterates through each suite recursively to find all tests. Applies a - * function in the format `fn(test)`. - * - * @see https://mochajs.org/api/Mocha.Suite.html#eachTest - */ - eachTest(fn: (test: Test) => void): this; - - /** - * This will run the root suite if we happen to be running in delayed mode. - * - * @see https://mochajs.org/api/Mocha.Suite.html#run - */ - run(): void; - - /** - * Generic hook-creator. - */ - protected _createHook(title: string, fn?: Func | AsyncFunc): Hook; - } - - // #region Suite "beforeAll" event - interface Suite extends NodeJS.EventEmitter { - on(event: "beforeAll", listener: (hook: Hook) => void): this; - once(event: "beforeAll", listener: (hook: Hook) => void): this; - addListener(event: "beforeAll", listener: (hook: Hook) => void): this; - removeListener(event: "beforeAll", listener: (hook: Hook) => void): this; - prependListener(event: "beforeAll", listener: (hook: Hook) => void): this; - prependOnceListener(event: "beforeAll", listener: (hook: Hook) => void): this; - emit(name: "beforeAll", hook: Hook): boolean; - } - // #endregion Suite "beforeAll" event - // #region Suite "afterAll" event - interface Suite extends NodeJS.EventEmitter { - on(event: "afterAll", listener: (hook: Hook) => void): this; - once(event: "afterAll", listener: (hook: Hook) => void): this; - addListener(event: "afterAll", listener: (hook: Hook) => void): this; - removeListener(event: "afterAll", listener: (hook: Hook) => void): this; - prependListener(event: "afterAll", listener: (hook: Hook) => void): this; - prependOnceListener(event: "afterAll", listener: (hook: Hook) => void): this; - emit(name: "afterAll", hook: Hook): boolean; - } - // #endregion Suite "afterAll" event - // #region Suite "beforeEach" event - interface Suite extends NodeJS.EventEmitter { - on(event: "beforeEach", listener: (hook: Hook) => void): this; - once(event: "beforeEach", listener: (hook: Hook) => void): this; - addListener(event: "beforeEach", listener: (hook: Hook) => void): this; - removeListener(event: "beforeEach", listener: (hook: Hook) => void): this; - prependListener(event: "beforeEach", listener: (hook: Hook) => void): this; - prependOnceListener(event: "beforeEach", listener: (hook: Hook) => void): this; - emit(name: "beforeEach", hook: Hook): boolean; - } - // #endregion Suite "beforeEach" event - // #region Suite "afterEach" event - interface Suite extends NodeJS.EventEmitter { - on(event: "afterEach", listener: (hook: Hook) => void): this; - once(event: "afterEach", listener: (hook: Hook) => void): this; - addListener(event: "afterEach", listener: (hook: Hook) => void): this; - removeListener(event: "afterEach", listener: (hook: Hook) => void): this; - prependListener(event: "afterEach", listener: (hook: Hook) => void): this; - prependOnceListener(event: "afterEach", listener: (hook: Hook) => void): this; - emit(name: "afterEach", hook: Hook): boolean; - } - // #endregion Suite "afterEach" event - // #region Suite "suite" event - interface Suite extends NodeJS.EventEmitter { - on(event: "suite", listener: (suite: Suite) => void): this; - once(event: "suite", listener: (suite: Suite) => void): this; - addListener(event: "suite", listener: (suite: Suite) => void): this; - removeListener(event: "suite", listener: (suite: Suite) => void): this; - prependListener(event: "suite", listener: (suite: Suite) => void): this; - prependOnceListener(event: "suite", listener: (suite: Suite) => void): this; - emit(name: "suite", suite: Suite): boolean; - } - // #endregion Suite "suite" event - // #region Suite "test" event - interface Suite { - on(event: "test", listener: (test: Test) => void): this; - once(event: "test", listener: (test: Test) => void): this; - addListener(event: "test", listener: (test: Test) => void): this; - removeListener(event: "test", listener: (test: Test) => void): this; - prependListener(event: "test", listener: (test: Test) => void): this; - prependOnceListener(event: "test", listener: (test: Test) => void): this; - emit(name: "test", test: Test): boolean; - } - // #endregion Suite "test" event - // #region Suite "run" event - interface Suite extends NodeJS.EventEmitter { - on(event: "run", listener: () => void): this; - once(event: "run", listener: () => void): this; - addListener(event: "run", listener: () => void): this; - removeListener(event: "run", listener: () => void): this; - prependListener(event: "run", listener: () => void): this; - prependOnceListener(event: "run", listener: () => void): this; - emit(name: "run"): boolean; - } - // #endregion Suite "run" event - // #region Suite "pre-require" event - interface Suite extends NodeJS.EventEmitter { - on(event: "pre-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - once(event: "pre-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - addListener(event: "pre-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - removeListener(event: "pre-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - prependListener(event: "pre-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - prependOnceListener(event: "pre-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - emit(name: "pre-require", context: MochaGlobals, file: string, mocha: Mocha): boolean; - } - // #endregion Suite "pre-require" event - // #region Suite "require" event - interface Suite extends NodeJS.EventEmitter { - on(event: "require", listener: (module: any, file: string, mocha: Mocha) => void): this; - once(event: "require", listener: (module: any, file: string, mocha: Mocha) => void): this; - addListener(event: "require", listener: (module: any, file: string, mocha: Mocha) => void): this; - removeListener(event: "require", listener: (module: any, file: string, mocha: Mocha) => void): this; - prependListener(event: "require", listener: (module: any, file: string, mocha: Mocha) => void): this; - prependOnceListener(event: "require", listener: (module: any, file: string, mocha: Mocha) => void): this; - emit(name: "require", module: any, file: string, mocha: Mocha): boolean; - } - // #endregion Suite "require" event - // #region Suite "post-require" event - interface Suite extends NodeJS.EventEmitter { - on(event: "post-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - once(event: "post-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - addListener(event: "post-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - removeListener(event: "post-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - prependListener(event: "post-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - prependOnceListener(event: "post-require", listener: (context: MochaGlobals, file: string, mocha: Mocha) => void): this; - emit(name: "post-require", context: MochaGlobals, file: string, mocha: Mocha): boolean; - } - // #endregion Suite "post-require" event - // #region Suite untyped events - interface Suite extends NodeJS.EventEmitter { - on(event: string, listener: (...args: any[]) => void): this; - once(event: string, listener: (...args: any[]) => void): this; - addListener(event: string, listener: (...args: any[]) => void): this; - removeListener(event: string, listener: (...args: any[]) => void): this; - prependListener(event: string, listener: (...args: any[]) => void): this; - prependOnceListener(event: string, listener: (...args: any[]) => void): this; - emit(name: string, ...args: any[]): boolean; - } - // #endregion Runner untyped events - - /** - * Initialize a new `Hook` with the given `title` and callback `fn` - * - * @see https://mochajs.org/api/Hook.html - */ - class Hook extends Runnable { - private _error; - - type: "hook"; - originalTitle?: string; // added by Runner - - /** - * Get the test `err`. - * - * @see https://mochajs.org/api/Hook.html#error - */ - error(): any; - - /** - * Set the test `err`. - * - * @see https://mochajs.org/api/Hook.html#error - */ - error(err: any): void; - } - - /** - * Initialize a new `Test` with the given `title` and callback `fn`. - * - * @see https://mochajs.org/api/Test.html - */ - class Test extends Runnable { - type: "test"; - speed?: "slow" | "medium" | "fast"; // added by reporters - err?: Error; // added by reporters - clone(): Test; - } - - /** - * Test statistics - */ - interface Stats { - suites: number; - tests: number; - passes: number; - pending: number; - failures: number; - start?: Date; - end?: Date; - duration?: number; - } - - type TestInterface = (suite: Suite) => void; - - interface ReporterConstructor { - new (runner: Runner, options: { reporterOptions?: any; }): reporters.Base; - } - - type Done = (err?: any) => void; - - /** - * Callback function used for tests and hooks. - */ - type Func = (this: Context, done: Done) => void; - - /** - * Async callback function used for tests and hooks. - */ - type AsyncFunc = (this: Context) => PromiseLike; - - /** - * Options to pass to Mocha. - */ - interface MochaOptions { - /** Test interfaces ("bdd", "tdd", "exports", etc.). */ - ui?: Interface; - - /** - * Reporter constructor, built-in reporter name, or reporter module path. Defaults to - * `"spec"`. - */ - reporter?: string | ReporterConstructor; - - /** Options to pass to the reporter. */ - reporterOptions?: any; - - /** Array of accepted globals. */ - globals?: string[]; - - /** timeout in milliseconds. */ - timeout?: number; - - enableTimeouts?: boolean; - - /** number of times to retry failed tests. */ - retries?: number; - - /** bail on the first test failure. */ - bail?: boolean; - - /** milliseconds to wait before considering a test slow. */ - slow?: number; - - /** ignore global leaks. */ - ignoreLeaks?: boolean; - - /** display the full stack trace on failure. */ - fullStackTrace?: boolean; - - /** string or regexp to filter tests with. */ - grep?: string | RegExp; - - /** Enable growl support. */ - growl?: boolean; - - /** Emit color output. */ - useColors?: boolean; - - /** Use inline diffs rather than +/-. */ - inlineDiffs?: boolean; - - /** Do not show diffs at all. */ - hideDiff?: boolean; - - asyncOnly?: boolean; - delay?: boolean; - forbidOnly?: boolean; - forbidPending?: boolean; - noHighlighting?: boolean; - allowUncaught?: boolean; - } - - interface MochaInstanceOptions extends MochaOptions { - files?: string[]; - } - - /** - * Variables added to the global scope by Mocha when run in the CLI. - */ - interface MochaGlobals { - /** - * Execute before running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#before - */ - before: HookFunction; - - /** - * Execute after running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#after - */ - after: HookFunction; - - /** - * Execute before each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#beforeEach - */ - beforeEach: HookFunction; - - /** - * Execute after each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#afterEach - */ - afterEach: HookFunction; - - /** - * Describe a "suite" containing nested suites and tests. - * - * - _Only available when invoked via the mocha CLI._ - */ - describe: SuiteFunction; - - /** - * Describe a "suite" containing nested suites and tests. - * - * - _Only available when invoked via the mocha CLI._ - */ - context: SuiteFunction; - - /** - * Pending suite. - * - * - _Only available when invoked via the mocha CLI._ - */ - xdescribe: PendingSuiteFunction; - - /** - * Pending suite. - * - * - _Only available when invoked via the mocha CLI._ - */ - xcontext: PendingSuiteFunction; - - /** - * Describes a test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - it: TestFunction; - - /** - * Describes a test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - specify: TestFunction; - - /** - * Describes a pending test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - xit: PendingTestFunction; - - /** - * Describes a pending test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - xspecify: PendingTestFunction; - - /** - * Execute before running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#before - */ - suiteSetup: HookFunction; - - /** - * Execute after running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#after - */ - suiteTeardown: HookFunction; - - /** - * Execute before each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#beforeEach - */ - setup: HookFunction; - - /** - * Execute after each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#afterEach - */ - teardown: HookFunction; - - /** - * Describe a "suite" containing nested suites and tests. - * - * - _Only available when invoked via the mocha CLI._ - */ - suite: SuiteFunction; - - /** - * Describes a test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - test: TestFunction; - - run: typeof run; - } - - /** - * Third-party declarations that want to add new entries to the `Reporter` union can - * contribute names here. - */ - interface ReporterContributions { - Base: never; - base: never; - Dot: never; - dot: never; - TAP: never; - tap: never; - JSON: never; - json: never; - HTML: never; - html: never; - List: never; - list: never; - Min: never; - min: never; - Spec: never; - spec: never; - Nyan: never; - nyan: never; - XUnit: never; - xunit: never; - Markdown: never; - markdown: never; - Progress: never; - progress: never; - Landing: never; - landing: never; - JSONStream: never; - "json-stream": never; - } - - type Reporter = keyof ReporterContributions; - - /** - * Third-party declarations that want to add new entries to the `Interface` union can - * contribute names here. - */ - interface InterfaceContributions { - bdd: never; - tdd: never; - qunit: never; - exports: never; - } - - type Interface = keyof InterfaceContributions; - - // #region Deprecations - - /** @deprecated use `Mocha.Context` instead. */ - interface IContext { - test?: IRunnable; - runnable(): IRunnable | undefined; - /** @deprecated `.runnable()` returns `this` in `Mocha.Context`. */ - runnable(runnable: IRunnable): IContext; - timeout(): number; - /** @deprecated `.timeout()` returns `this` in `Mocha.Context`. */ - timeout(timeout: number): IContext; - /** @deprecated `.enableTimeouts()` has additional overloads in `Mocha.Context`. */ - /** @deprecated `.enableTimeouts()` returns `this` in `Mocha.Context`. */ - enableTimeouts(enableTimeouts: boolean): IContext; - /** @deprecated `.slow()` has additional overloads in `Mocha.Context`. */ - /** @deprecated `.slow()` returns `this` in `Mocha.Context`. */ - slow(slow: number): IContext; - /** @deprecated `.skip()` returns `never` in `Mocha.Context`. */ - skip(): IContext; - retries(): number; - /** @deprecated `.retries()` returns `this` in `Mocha.Context`. */ - retries(retries: number): IContext; - } - - /** @deprecated use `Mocha.Suite` instead. */ - interface ISuiteCallbackContext { - /** @deprecated `.timeout()` has additional overloads in `Mocha.Suite`. */ - timeout(ms: number | string): this; - /** @deprecated `.retries()` has additional overloads in `Mocha.Suite`. */ - retries(n: number): this; - /** @deprecated `.slow()` has additional overloads in `Mocha.Suite`. */ - slow(ms: number): this; - } - - /** @deprecated use `Mocha.Context` instead. */ - interface IHookCallbackContext { - /** @deprecated `.skip()` returns `never` in `Mocha.Context`. */ - skip(): this; - /** @deprecated `.timeout()` has additional overloads in `Mocha.Context`. */ - timeout(ms: number | string): this; - [index: string]: any; - } - - /** @deprecated use `Mocha.Context` instead. */ - interface ITestCallbackContext { - /** @deprecated `.skip()` returns `never` in `Mocha.Context`. */ - skip(): this; - /** @deprecated `.timeout()` has additional overloads in `Mocha.Context`. */ - timeout(ms: number | string): this; - /** @deprecated `.retries()` has additional overloads in `Mocha.Context`. */ - retries(n: number): this; - /** @deprecated `.slow()` has additional overloads in `Mocha.Context`. */ - slow(ms: number): this; - [index: string]: any; - } - - /** Partial interface for Mocha's `Runnable` class. */ - /** @deprecated use `Mocha.Runnable` instead. */ - interface IRunnable extends NodeJS.EventEmitter { - title: string; - /** @deprecated `.fn` has type `Func | AsyncFunc` in `Mocha.Runnable`. */ - fn: Function | undefined; - async: boolean; - sync: boolean; - timedOut: boolean; - /** @deprecated `.timeout()` has additional overloads in `Mocha.Runnable`. */ - timeout(n: number | string): this; - duration?: number; - } - - /** Partial interface for Mocha's `Suite` class. */ - /** @deprecated use `Mocha.Suite` instead. */ - interface ISuite { - /** @deprecated `.ctx` has type `Mocha.Context` in `Mocha.Suite`. */ - ctx: IContext; - /** @deprecated `.parent` has type `Mocha.Suite | undefined` in `Mocha.Suite`. */ - parent: ISuite | undefined; - root: boolean; - title: string; - /** @deprecated `.suites` has type `Mocha.Suite[]` in `Mocha.Suite`. */ - suites: ISuite[]; - /** @deprecated `.tests` has type `Mocha.Test[]` in `Mocha.Suite`. */ - tests: ITest[]; - - bail(): boolean; - /** @deprecated `.bail()` returns `this` in `Mocha.Suite`. */ - bail(bail: boolean): ISuite; - fullTitle(): string; - retries(): number; - /** @deprecated `.retries()` returns `this` in `Mocha.Suite`. */ - retries(retries: number): ISuite; - slow(): number; - /** @deprecated `.slow()` returns `this` in `Mocha.Suite`. */ - slow(slow: number): ISuite; - timeout(): number; - /** @deprecated `.timeout()` returns `this` in `Mocha.Suite`. */ - timeout(timeout: number): ISuite; - } - - /** Partial interface for Mocha's `Test` class. */ - /** @deprecated use `Mocha.Test` instead. */ - interface ITest extends IRunnable { - body?: string; - file?: string; - /** @deprecated `.parent` has type `Mocha.Suite | undefined` in `Mocha.Test`. */ - parent?: ISuite; - pending: boolean; - state?: 'failed' | 'passed'; - type: 'test'; - fullTitle(): string; - } - - /** @deprecated use `Mocha.Hook` instead. */ - interface IHook extends IRunnable { - /** @deprecated `.ctx` has type `Mocha.Context` in `Mocha.Runnable`. */ - ctx?: IContext; - /** @deprecated `.parent` has type `Mocha.Suite` in `Mocha.Runnable`. */ - parent?: ISuite; - type: 'hook'; - /** @deprecated `.error()` has additional overloads in `Mocha.Hook`. */ - error(err: Error): void; - } - - /** @deprecated use `Mocha.Context` instead. */ - interface IBeforeAndAfterContext extends IHookCallbackContext { - /** @deprecated `.currentTest` has type `Mocha.Test` in `Mocha.Context`. */ - currentTest?: ITest; - } - - /** @deprecated use `Mocha.Stats` instead. */ - type IStats = Stats; - - /** Partial interface for Mocha's `Runner` class. */ - /** @deprecated use `Mocha.Runner` instead. */ - interface IRunner extends NodeJS.EventEmitter { - asyncOnly?: boolean; - stats?: IStats; - started: boolean; - /** @deprecated `.suite` has type `Mocha.Suite` in `Mocha.Runner`. */ - suite: ISuite; - total: number; - failures: number; - forbidOnly?: boolean; - forbidPending?: boolean; - fullStackTrace?: boolean; - ignoreLeaks?: boolean; - grep(re: RegExp, invert: boolean): this; - /** @deprecated Parameter `suite` has type `Mocha.Suite` in `Mocha.Runner`. */ - grepTotal(suite: ISuite): number; - /** @deprecated `.globals()` has different overloads in `Mocha.Runner`. */ - globals(arr: ReadonlyArray): this | string[]; - abort(): this; - run(fn?: (failures: number) => void): this; - } - - /** @deprecated use `Mocha.SuiteFunction` instead. */ - interface IContextDefinition { - /** @deprecated use `Mocha.SuiteFunction` instead. */ - (description: string, callback: (this: ISuiteCallbackContext) => void): ISuite; - /** @deprecated use `Mocha.SuiteFunction` instead. */ - only(description: string, callback: (this: ISuiteCallbackContext) => void): ISuite; - /** @deprecated use `Mocha.SuiteFunction` instead. */ - skip(description: string, callback: (this: ISuiteCallbackContext) => void): void; - } - - /** @deprecated use `Mocha.TestFunction` instead. */ - interface ITestDefinition { - /** @deprecated use `Mocha.TestFunction` instead. */ - /** @deprecated `Mocha.TestFunction` does not allow mixing `done` with a return type of `PromiseLike`. */ - (expectation: string, callback?: (this: ITestCallbackContext, done: MochaDone) => PromiseLike | void): ITest; - /** @deprecated use `Mocha.TestFunction` instead. */ - /** @deprecated `Mocha.TestFunction#only` does not allow mixing `done` with a return type of `PromiseLike`. */ - only(expectation: string, callback?: (this: ITestCallbackContext, done: MochaDone) => PromiseLike | void): ITest; - /** @deprecated use `Mocha.TestFunction` instead. */ - /** @deprecated `Mocha.TestFunction#skip` does not allow mixing `done` with a return type of `PromiseLike`. */ - skip(expectation: string, callback?: (this: ITestCallbackContext, done: MochaDone) => PromiseLike | void): void; - } - - // #endregion -} - -declare global { - // #region Test interface augmentations - - /** - * Triggers root suite execution. - * - * - _Only available if flag --delay is passed into Mocha._ - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#runWithSuite - */ - function run(): void; - - /** - * Execute before running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#before - */ - var before: Mocha.HookFunction; - - /** - * Execute before running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#before - */ - var suiteSetup: Mocha.HookFunction; - - /** - * Execute after running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#after - */ - var after: Mocha.HookFunction; - - /** - * Execute after running tests. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#after - */ - var suiteTeardown: Mocha.HookFunction; - - /** - * Execute before each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#beforeEach - */ - var beforeEach: Mocha.HookFunction; - - /** - * Execute before each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#beforeEach - */ - var setup: Mocha.HookFunction; - - /** - * Execute after each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#afterEach - */ - var afterEach: Mocha.HookFunction; - - /** - * Execute after each test case. - * - * - _Only available when invoked via the mocha CLI._ - * - * @see https://mochajs.org/api/global.html#afterEach - */ - var teardown: Mocha.HookFunction; - - /** - * Describe a "suite" containing nested suites and tests. - * - * - _Only available when invoked via the mocha CLI._ - */ - var describe: Mocha.SuiteFunction; - - /** - * Describe a "suite" containing nested suites and tests. - * - * - _Only available when invoked via the mocha CLI._ - */ - var context: Mocha.SuiteFunction; - - /** - * Describe a "suite" containing nested suites and tests. - * - * - _Only available when invoked via the mocha CLI._ - */ - var suite: Mocha.SuiteFunction; - - /** - * Pending suite. - * - * - _Only available when invoked via the mocha CLI._ - */ - var xdescribe: Mocha.PendingSuiteFunction; - - /** - * Pending suite. - * - * - _Only available when invoked via the mocha CLI._ - */ - var xcontext: Mocha.PendingSuiteFunction; - - /** - * Describes a test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - var it: Mocha.TestFunction; - - /** - * Describes a test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - var specify: Mocha.TestFunction; - - /** - * Describes a test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - var test: Mocha.TestFunction; - - /** - * Describes a pending test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - var xit: Mocha.PendingTestFunction; - - /** - * Describes a pending test case. - * - * - _Only available when invoked via the mocha CLI._ - */ - var xspecify: Mocha.PendingTestFunction; - - // #endregion Test interface augmentations - - // #region Reporter augmentations - - // Forward declaration for `HTMLLIElement` from lib.dom.d.ts. - // Required by Mocha.reporters.HTML. - // NOTE: Mocha *must not* have a direct dependency on DOM types. - // tslint:disable-next-line no-empty-interface - interface HTMLLIElement { } - - // Augments the DOM `Window` object when lib.dom.d.ts is loaded. - // tslint:disable-next-line no-empty-interface - interface Window extends Mocha.MochaGlobals { } - - namespace NodeJS { - // Forward declaration for `NodeJS.EventEmitter` from node.d.ts. - // Required by Mocha.Runnable, Mocha.Runner, and Mocha.Suite. - // NOTE: Mocha *must not* have a direct dependency on @types/node. - // tslint:disable-next-line no-empty-interface - interface EventEmitter { } - - // Augments NodeJS's `global` object when node.d.ts is loaded - // tslint:disable-next-line no-empty-interface - interface Global extends Mocha.MochaGlobals { } - } - - // #endregion Reporter augmentations - - // #region Browser augmentations - - /** - * Mocha global. - * - * - _Only supported in the browser._ - */ - const mocha: BrowserMocha; - - interface BrowserMocha extends Mocha { - /** - * Function to allow assertion libraries to throw errors directly into mocha. - * This is useful when running tests in a browser because window.onerror will - * only receive the 'message' attribute of the Error. - * - * - _Only supported in the browser._ - */ - throwError(err: any): never; - - /** - * Setup mocha with the given settings options. - * - * - _Only supported in the browser._ - */ - setup(opts?: Mocha.Interface | MochaSetupOptions): this; - } - - /** - * Options to pass to `mocha.setup` in the browser. - */ - interface MochaSetupOptions extends Mocha.MochaOptions { - // TODO: This does not seem to be supported according to the source. Should it be removed? - require?: string[]; - fullTrace?: boolean; - } - - // #endregion Browser augmentations - - // #region Deprecations - - /** @deprecated use `Mocha.DoneCallback` instead. */ - type MochaDone = Mocha.Done; - - /** @deprecated use `Mocha.ReporterConstructor` instead. */ - type ReporterConstructor = Mocha.ReporterConstructor; - - // #endregion Deprecations -} diff --git a/scripts/types/mocha/lib/interfaces/common.d.ts b/scripts/types/mocha/lib/interfaces/common.d.ts deleted file mode 100644 index 1deb9712e6b..00000000000 --- a/scripts/types/mocha/lib/interfaces/common.d.ts +++ /dev/null @@ -1,109 +0,0 @@ -import Mocha = require("../../"); - -export = common; - -declare function common(suites: Mocha.Suite[], context: Mocha.MochaGlobals, mocha: Mocha): common.CommonFunctions; - -declare namespace common { - export interface CommonFunctions { - /** - * This is only present if flag --delay is passed into Mocha. It triggers - * root suite execution. - */ - runWithSuite(suite: Mocha.Suite): () => void; - - /** - * Execute before running tests. - */ - before(fn?: Mocha.Func | Mocha.AsyncFunc): void; - - /** - * Execute before running tests. - */ - before(name: string, fn?: Mocha.Func | Mocha.AsyncFunc): void; - - /** - * Execute after running tests. - */ - after(fn?: Mocha.Func | Mocha.AsyncFunc): void; - - /** - * Execute after running tests. - */ - after(name: string, fn?: Mocha.Func | Mocha.AsyncFunc): void; - - /** - * Execute before each test case. - */ - beforeEach(fn?: Mocha.Func | Mocha.AsyncFunc): void; - - /** - * Execute before each test case. - */ - beforeEach(name: string, fn?: Mocha.Func | Mocha.AsyncFunc): void; - - /** - * Execute after each test case. - */ - afterEach(fn?: Mocha.Func | Mocha.AsyncFunc): void; - - /** - * Execute after each test case. - */ - afterEach(name: string, fn?: Mocha.Func | Mocha.AsyncFunc): void; - - suite: SuiteFunctions; - test: TestFunctions; - } - - export interface CreateOptions { - /** Title of suite */ - title: string; - - /** Suite function */ - fn?: (this: Mocha.Suite) => void; - - /** Is suite pending? */ - pending?: boolean; - - /** Filepath where this Suite resides */ - file?: string; - - /** Is suite exclusive? */ - isOnly?: boolean; - } - - export interface SuiteFunctions { - /** - * Create an exclusive Suite; convenience function - */ - only(opts: CreateOptions): Mocha.Suite; - - /** - * Create a Suite, but skip it; convenience function - */ - skip(opts: CreateOptions): Mocha.Suite; - - /** - * Creates a suite. - */ - create(opts: CreateOptions): Mocha.Suite; - } - - export interface TestFunctions { - /** - * Exclusive test-case. - */ - only(mocha: Mocha, test: Mocha.Test): Mocha.Test; - - /** - * Pending test case. - */ - skip(title: string): void; - - /** - * Number of retry attempts - */ - retries(n: number): void; - } -} diff --git a/scripts/types/mocha/lib/ms.d.ts b/scripts/types/mocha/lib/ms.d.ts deleted file mode 100644 index 890da5b8c5a..00000000000 --- a/scripts/types/mocha/lib/ms.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -export = milliseconds; - -/** - * Parse the given `str` and return milliseconds. - * - * @see {@link https://mochajs.org/api/module-milliseconds.html} - * @see {@link https://mochajs.org/api/module-milliseconds.html#~parse} - */ -declare function milliseconds(val: string): number; - -/** - * Format for `ms`. - * - * @see {@link https://mochajs.org/api/module-milliseconds.html} - * @see {@link https://mochajs.org/api/module-milliseconds.html#~format} - */ -declare function milliseconds(val: number): string; diff --git a/scripts/types/mocha/package.json b/scripts/types/mocha/package.json deleted file mode 100644 index e45485e803b..00000000000 --- a/scripts/types/mocha/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "@types/mocha", - "private": true, - "version": "5.2.1" -} diff --git a/src/harness/harness.ts b/src/harness/harness.ts index a78a04c3ecb..f03103d0239 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -47,23 +47,6 @@ interface XMLHttpRequest { /* tslint:enable:no-var-keyword prefer-const */ namespace Utils { - // Setup some globals based on the current environment - export const enum ExecutionEnvironment { - Node, - Browser, - } - - export function getExecutionEnvironment() { - if (typeof window !== "undefined") { - return ExecutionEnvironment.Browser; - } - else { - return ExecutionEnvironment.Node; - } - } - - export let currentExecutionEnvironment = getExecutionEnvironment(); - export function encodeString(s: string): string { return ts.sys.bufferFrom!(s).toString("utf8"); } @@ -74,23 +57,12 @@ namespace Utils { } export function evalFile(fileContents: string, fileName: string, nodeContext?: any) { - const environment = getExecutionEnvironment(); - switch (environment) { - case ExecutionEnvironment.Browser: - // tslint:disable-next-line:no-eval - eval(fileContents); - break; - case ExecutionEnvironment.Node: - const vm = require("vm"); - if (nodeContext) { - vm.runInNewContext(fileContents, nodeContext, fileName); - } - else { - vm.runInThisContext(fileContents, fileName); - } - break; - default: - throw new Error("Unknown context"); + const vm = require("vm"); + if (nodeContext) { + vm.runInNewContext(fileContents, nodeContext, fileName); + } + else { + vm.runInThisContext(fileContents, fileName); } } @@ -624,344 +596,11 @@ namespace Harness { }; } - interface URL { - hash: string; - host: string; - hostname: string; - href: string; - password: string; - pathname: string; - port: string; - protocol: string; - search: string; - username: string; - toString(): string; - } - - declare var URL: { - prototype: URL; - new(url: string, base?: string | URL): URL; - }; - - function createBrowserIO(): IO { - const serverRoot = new URL("http://localhost:8888/"); - - class HttpHeaders extends collections.SortedMap { - constructor(template?: Record) { - super(ts.compareStringsCaseInsensitive); - if (template) { - for (const key in template) { - if (ts.hasProperty(template, key)) { - this.set(key, template[key]); - } - } - } - } - - public static combine(left: HttpHeaders | undefined, right: HttpHeaders | undefined): HttpHeaders | undefined { - if (!left && !right) return undefined; - const headers = new HttpHeaders(); - if (left) left.forEach((value, key) => { headers.set(key, value); }); - if (right) right.forEach((value, key) => { headers.set(key, value); }); - return headers; - } - - public has(key: string) { - return super.has(key.toLowerCase()); - } - - public get(key: string) { - return super.get(key.toLowerCase()); - } - - public set(key: string, value: string | string[]) { - return super.set(key.toLowerCase(), value); - } - - public delete(key: string) { - return super.delete(key.toLowerCase()); - } - - public writeRequestHeaders(xhr: XMLHttpRequest) { - this.forEach((values, key) => { - if (key === "access-control-allow-origin" || key === "content-length") return; - const value = Array.isArray(values) ? values.join(",") : values; - if (key === "content-type") { - xhr.overrideMimeType(value); - return; - } - xhr.setRequestHeader(key, value); - }); - } - - public static readResponseHeaders(xhr: XMLHttpRequest): HttpHeaders { - const allHeaders = xhr.getAllResponseHeaders(); - const headers = new HttpHeaders(); - for (const header of allHeaders.split(/\r\n/g)) { - const colonIndex = header.indexOf(":"); - if (colonIndex >= 0) { - const key = header.slice(0, colonIndex).trim(); - const value = header.slice(colonIndex + 1).trim(); - const values = value.split(","); - headers.set(key, values.length > 1 ? values : value); - } - } - return headers; - } - } - - class HttpContent { - public headers: HttpHeaders; - public content: string; - - constructor(headers: HttpHeaders | Record, content: string) { - this.headers = headers instanceof HttpHeaders ? headers : new HttpHeaders(headers); - this.content = content; - } - - public static fromMediaType(mediaType: string, content: string) { - return new HttpContent({ "Content-Type": mediaType }, content); - } - - public static text(content: string) { - return HttpContent.fromMediaType("text/plain", content); - } - - public static json(content: object) { - return HttpContent.fromMediaType("application/json", JSON.stringify(content)); - } - - public static readResponseContent(xhr: XMLHttpRequest) { - if (typeof xhr.responseText === "string") { - return new HttpContent({ - "Content-Type": xhr.getResponseHeader("Content-Type") || undefined!, // TODO: GH#18217 - "Content-Length": xhr.getResponseHeader("Content-Length") || undefined!, // TODO: GH#18217 - }, xhr.responseText); - } - return undefined; - } - - public writeRequestHeaders(xhr: XMLHttpRequest) { - this.headers.writeRequestHeaders(xhr); - } - } - - class HttpRequestMessage { - public method: string; - public url: URL; - public headers: HttpHeaders; - public content?: HttpContent; - - constructor(method: string, url: string | URL, headers?: HttpHeaders | Record, content?: HttpContent) { - this.method = method; - this.url = typeof url === "string" ? new URL(url) : url; - this.headers = headers instanceof HttpHeaders ? headers : new HttpHeaders(headers); - this.content = content; - } - - public static options(url: string | URL) { - return new HttpRequestMessage("OPTIONS", url); - } - - public static head(url: string | URL) { - return new HttpRequestMessage("HEAD", url); - } - - public static get(url: string | URL) { - return new HttpRequestMessage("GET", url); - } - - public static delete(url: string | URL) { - return new HttpRequestMessage("DELETE", url); - } - - public static put(url: string | URL, content: HttpContent) { - return new HttpRequestMessage("PUT", url, /*headers*/ undefined, content); - } - - public static post(url: string | URL, content: HttpContent) { - return new HttpRequestMessage("POST", url, /*headers*/ undefined, content); - } - - public writeRequestHeaders(xhr: XMLHttpRequest) { - this.headers.writeRequestHeaders(xhr); - if (this.content) { - this.content.writeRequestHeaders(xhr); - } - } - } - - class HttpResponseMessage { - public statusCode: number; - public statusMessage: string; - public headers: HttpHeaders; - public content?: HttpContent; - - constructor(statusCode: number, statusMessage: string, headers?: HttpHeaders | Record, content?: HttpContent) { - this.statusCode = statusCode; - this.statusMessage = statusMessage; - this.headers = headers instanceof HttpHeaders ? headers : new HttpHeaders(headers); - this.content = content; - } - - public static notFound(): HttpResponseMessage { - return new HttpResponseMessage(404, "Not Found"); - } - - public static hasSuccessStatusCode(response: HttpResponseMessage) { - return response.statusCode === 304 || (response.statusCode >= 200 && response.statusCode < 300); - } - - public static readResponseMessage(xhr: XMLHttpRequest) { - return new HttpResponseMessage( - xhr.status, - xhr.statusText, - HttpHeaders.readResponseHeaders(xhr), - HttpContent.readResponseContent(xhr)); - } - } - - function send(request: HttpRequestMessage): HttpResponseMessage { - const xhr = new XMLHttpRequest(); - try { - xhr.open(request.method, request.url.toString(), /*async*/ false); - request.writeRequestHeaders(xhr); - xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); - xhr.send(request.content && request.content.content); - while (xhr.readyState !== 4); // block until ready - return HttpResponseMessage.readResponseMessage(xhr); - } - catch (e) { - return HttpResponseMessage.notFound(); - } - } - - let caseSensitivity: "CI" | "CS" | undefined; - - function useCaseSensitiveFileNames() { - if (!caseSensitivity) { - const response = send(HttpRequestMessage.options(new URL("*", serverRoot))); - const xCaseSensitivity = response.headers.get("X-Case-Sensitivity"); - caseSensitivity = xCaseSensitivity === "CS" ? "CS" : "CI"; - } - return caseSensitivity === "CS"; - } - - function resolvePath(path: string) { - const response = send(HttpRequestMessage.post(new URL("/api/resolve", serverRoot), HttpContent.text(path))); - return HttpResponseMessage.hasSuccessStatusCode(response) && response.content ? response.content.content : undefined; - } - - function getFileSize(path: string): number { - const response = send(HttpRequestMessage.head(new URL(path, serverRoot))); - return HttpResponseMessage.hasSuccessStatusCode(response) ? +response.headers.get("Content-Length")!.toString() : 0; - } - - function readFile(path: string): string | undefined { - const response = send(HttpRequestMessage.get(new URL(path, serverRoot))); - return HttpResponseMessage.hasSuccessStatusCode(response) && response.content ? response.content.content : undefined; - } - - function writeFile(path: string, contents: string) { - send(HttpRequestMessage.put(new URL(path, serverRoot), HttpContent.text(contents))); - } - - function fileExists(path: string): boolean { - const response = send(HttpRequestMessage.head(new URL(path, serverRoot))); - return HttpResponseMessage.hasSuccessStatusCode(response); - } - - function directoryExists(path: string): boolean { - const response = send(HttpRequestMessage.post(new URL("/api/directoryExists", serverRoot), HttpContent.text(path))); - return hasJsonContent(response) && JSON.parse(response.content.content) as boolean; - } - - function deleteFile(path: string) { - send(HttpRequestMessage.delete(new URL(path, serverRoot))); - } - - function directoryName(path: string) { - const url = new URL(path, serverRoot); - return ts.getDirectoryPath(ts.normalizeSlashes(url.pathname || "/")); - } - - function enumerateTestFiles(runner: RunnerBase): (string | FileBasedTest)[] { - const response = send(HttpRequestMessage.post(new URL("/api/enumerateTestFiles", serverRoot), HttpContent.text(runner.kind()))); - return hasJsonContent(response) ? JSON.parse(response.content.content) : []; - } - - function listFiles(dirname: string, spec?: RegExp, options?: { recursive?: boolean }): string[] { - if (spec || (options && !options.recursive)) { - let results = IO.listFiles(dirname); - if (spec) { - results = results.filter(file => spec.test(file)); - } - if (options && !options.recursive) { - results = results.filter(file => ts.getDirectoryPath(ts.normalizeSlashes(file)) === dirname); - } - return results; - } - - const response = send(HttpRequestMessage.post(new URL("/api/listFiles", serverRoot), HttpContent.text(dirname))); - return hasJsonContent(response) ? JSON.parse(response.content.content) : []; - } - - function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[], depth?: number) { - return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), "", depth, getAccessibleFileSystemEntries); - } - - function getAccessibleFileSystemEntries(dirname: string): ts.FileSystemEntries { - const response = send(HttpRequestMessage.post(new URL("/api/getAccessibleFileSystemEntries", serverRoot), HttpContent.text(dirname))); - return hasJsonContent(response) ? JSON.parse(response.content.content) : { files: [], directories: [] }; - } - - function hasJsonContent(response: HttpResponseMessage): response is HttpResponseMessage & { content: HttpContent } { - return HttpResponseMessage.hasSuccessStatusCode(response) - && !!response.content - && /^application\/json(;.*)$/.test("" + response.content.headers.get("Content-Type")); - } - - return { - newLine: () => harnessNewLine, - getCurrentDirectory: () => "", - useCaseSensitiveFileNames, - resolvePath, - getFileSize, - readFile, - writeFile, - directoryName: Utils.memoize(directoryName, path => path), - getDirectories: () => [], - createDirectory: () => {}, // tslint:disable-line no-empty - fileExists, - directoryExists, - deleteFile, - listFiles: Utils.memoize(listFiles, (path, spec, options) => `${path}|${spec}|${options ? options.recursive === true : true}`), - enumerateTestFiles: Utils.memoize(enumerateTestFiles, runner => runner.kind()), - log: s => console.log(s), - args: () => [], - getExecutingFilePath: () => "", - exit: () => {}, // tslint:disable-line no-empty - readDirectory, - getAccessibleFileSystemEntries, - getWorkspaceRoot: () => "/" - }; - } - export function mockHash(s: string): string { return `hash-${s}`; } - const environment = Utils.getExecutionEnvironment(); - switch (environment) { - case Utils.ExecutionEnvironment.Node: - IO = createNodeIO(); - break; - case Utils.ExecutionEnvironment.Browser: - IO = createBrowserIO(); - break; - default: - throw new Error(`Unknown value '${environment}' for ExecutionEnvironment.`); - } + IO = createNodeIO(); } if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.getEnvironmentVariable!("NODE_ENV"))) { @@ -970,10 +609,8 @@ if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.ge namespace Harness { export const libFolder = "built/local/"; - const tcServicesFileName = ts.combinePaths(libFolder, Utils.getExecutionEnvironment() === Utils.ExecutionEnvironment.Browser ? "typescriptServicesInBrowserTest.js" : "typescriptServices.js"); - export const tcServicesFile = IO.readFile(tcServicesFileName) + (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser - ? IO.newLine() + `//# sourceURL=${IO.resolvePath(tcServicesFileName)}` - : ""); + const tcServicesFileName = ts.combinePaths(libFolder, "typescriptServices.js"); + export const tcServicesFile = IO.readFile(tcServicesFileName) + IO.newLine() + `//# sourceURL=${IO.resolvePath(tcServicesFileName)}`; export type SourceMapEmitterCallback = ( emittedFile: string, diff --git a/src/testRunner/parallel/host.ts b/src/testRunner/parallel/host.ts index 597013ed0d9..3e531561e58 100644 --- a/src/testRunner/parallel/host.ts +++ b/src/testRunner/parallel/host.ts @@ -538,25 +538,23 @@ namespace Harness.Parallel.Host { let xunitReporter: import("mocha").reporters.XUnit | undefined; let failedTestReporter: import("../../../scripts/failed-tests") | undefined; - if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser) { - if (process.env.CI === "true") { - xunitReporter = new Mocha.reporters.XUnit(replayRunner, { - reporterOptions: { - suiteName: "Tests", - output: "./TEST-results.xml" - } - }); - patchStats(xunitReporter.stats); - xunitReporter.write(`\n`); - } - else { - failedTestReporter = new FailedTestReporter(replayRunner, { - reporterOptions: { - file: path.resolve(".failed-tests"), - keepFailed - } - }); - } + if (process.env.CI === "true") { + xunitReporter = new Mocha.reporters.XUnit(replayRunner, { + reporterOptions: { + suiteName: "Tests", + output: "./TEST-results.xml" + } + }); + patchStats(xunitReporter.stats); + xunitReporter.write(`\n`); + } + else { + failedTestReporter = new FailedTestReporter(replayRunner, { + reporterOptions: { + file: path.resolve(".failed-tests"), + keepFailed + } + }); } const savedUseColors = Base.useColors; diff --git a/src/testRunner/runner.ts b/src/testRunner/runner.ts index e528678ab83..be46d935297 100644 --- a/src/testRunner/runner.ts +++ b/src/testRunner/runner.ts @@ -182,10 +182,7 @@ function handleTestConfig() { runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance)); runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions)); - // TODO: project tests don"t work in the browser yet - if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser) { - runners.push(new project.ProjectRunner()); - } + runners.push(new project.ProjectRunner()); // language services runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Native)); @@ -195,7 +192,7 @@ function handleTestConfig() { // runners.push(new GeneratedFourslashRunner()); // CRON-only tests - if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser && process.env.TRAVIS_EVENT_TYPE === "cron") { + if (process.env.TRAVIS_EVENT_TYPE === "cron") { runners.push(new UserCodeRunner()); } } @@ -229,13 +226,11 @@ function beginTests() { let isWorker: boolean; function startTestEnvironment() { isWorker = handleTestConfig(); - if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser) { - if (isWorker) { - return Harness.Parallel.Worker.start(); - } - else if (taskConfigsFolder && workerCount && workerCount > 1) { - return Harness.Parallel.Host.start(); - } + if (isWorker) { + return Harness.Parallel.Worker.start(); + } + else if (taskConfigsFolder && workerCount && workerCount > 1) { + return Harness.Parallel.Host.start(); } beginTests(); } diff --git a/tests/perfsys.ts b/tests/perfsys.ts deleted file mode 100644 index abe09c88a0f..00000000000 --- a/tests/perfsys.ts +++ /dev/null @@ -1,104 +0,0 @@ -/// -/// - -namespace perftest { - interface IOLog { - resolvePath: ts.Map; - fileNames: string[]; - } - - export interface IO { - getOut(): string; - } - - export const readFile = ts.sys.readFile; - const writeFile = ts.sys.writeFile; - export const write = ts.sys.write; - const resolvePath = ts.sys.resolvePath; - export const getExecutingFilePath = ts.sys.getExecutingFilePath; - export const getCurrentDirectory = ts.sys.getCurrentDirectory; - // const exit = ts.sys.exit; - - const args = ts.sys.args; - - // augment sys so first ts.executeCommandLine call will be finish silently - ts.sys.write = (s: string) => { }; - ts.sys.exit = (code: number) => { }; - ts.sys.args = []; - - export function restoreSys() { - ts.sys.args = args; - ts.sys.write = write; - } - - export function hasLogIOFlag() { - return args.length > 2 && args[0] === "--logio"; - } - - export function getArgsWithoutLogIOFlag() { - return args.slice(2); - } - - export function getArgsWithoutIOLogFile() { - return args.slice(1); - } - - const resolvePathLog: ts.Map = {}; - - export function interceptIO() { - ts.sys.resolvePath = (s) => { - const result = resolvePath(s); - resolvePathLog[s] = result; - return result; - }; - } - - export function writeIOLog(fileNames: string[]) { - const path = args[1]; - const log: IOLog = { - fileNames: fileNames, - resolvePath: resolvePathLog - }; - - writeFile(path, JSON.stringify(log)); - } - - export function prepare(): IO { - const log = JSON.parse(readFile(args[0])); - - const files: ts.Map = {}; - log.fileNames.forEach(f => { files[f] = readFile(f); }); - - ts.sys.createDirectory = (s: string) => { }; - ts.sys.directoryExists = (s: string) => true; - ts.sys.fileExists = (s: string) => true; - - const currentDirectory = ts.sys.getCurrentDirectory(); - ts.sys.getCurrentDirectory = () => currentDirectory; - - const executingFilePath = ts.sys.getExecutingFilePath(); - ts.sys.getExecutingFilePath = () => executingFilePath; - - ts.sys.readFile = (s: string) => { - return files[s]; - }; - - ts.sys.resolvePath = (s: string) => { - const path = log.resolvePath[s]; - if (!path) { - throw new Error("Unexpected path '" + s + "'"); - } - return path; - }; - - ts.sys.writeFile = (path: string, data: string) => { }; - - let out = ""; - - ts.sys.write = (s: string) => { out += s; }; - - return { - getOut: () => out, - }; - } -} diff --git a/tests/perftsc.ts b/tests/perftsc.ts deleted file mode 100644 index 0753023853e..00000000000 --- a/tests/perftsc.ts +++ /dev/null @@ -1,30 +0,0 @@ -/// -/// - -// resolve all files used in this compilation -if (perftest.hasLogIOFlag()) { - perftest.interceptIO(); - - const compilerHost: ts.CompilerHost = { - getSourceFile: (s, v) => { - const content = perftest.readFile(s); - return content !== undefined ? ts.createSourceFile(s, content, v) : undefined; - }, - getDefaultLibFileName: () => ts.combinePaths(ts.getDirectoryPath(ts.normalizePath(perftest.getExecutingFilePath())), "lib.d.ts"), - writeFile: (f: string, content: string) => { throw new Error("Unexpected operation: writeFile"); }, - getCurrentDirectory: () => perftest.getCurrentDirectory(), - getCanonicalFileName: (f: string) => ts.sys.useCaseSensitiveFileNames ? f : f.toLowerCase(), - useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames, - getNewLine: () => ts.sys.newLine - }; - - const commandLine = ts.parseCommandLine(perftest.getArgsWithoutLogIOFlag()); - const program = ts.createProgram(commandLine.fileNames, commandLine.options, compilerHost); - const fileNames = program.getSourceFiles().map(f => f.fileName); - perftest.writeIOLog(fileNames); -} -else { - const io = perftest.prepare(); - ts.executeCommandLine(perftest.getArgsWithoutIOLogFile()); - perftest.write(io.getOut()); -} diff --git a/tests/perftsc.tsconfig.json b/tests/perftsc.tsconfig.json deleted file mode 100644 index dafd33f9def..00000000000 --- a/tests/perftsc.tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "module": "commonjs", - "declaration": false, - "removeComments": true, - "noResolve": false, - "stripInternal": false, - "sourceMap": true, - "outFile": "../built/local/perftsc.js" - }, - "files": [ - "perftsc.ts" - ] -} \ No newline at end of file diff --git a/tests/test.bat b/tests/test.bat deleted file mode 100644 index 4517d99f3c8..00000000000 --- a/tests/test.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off -setlocal -set LF=^ - - -for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a" - -setlocal enableDelayedExpansion -echo "START" -echo "asdf!CR!asdf" -echo "AASDF!LF!ASDF" -echo "END" -findstr /S /R /M /C:"[^!CR!]!LF!" * diff --git a/tests/webTestResults.html b/tests/webTestResults.html deleted file mode 100644 index 487abfc117d..00000000000 --- a/tests/webTestResults.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - Mocha Tests - - - - - -
- -
-
- - - - - - - - - \ No newline at end of file diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts deleted file mode 100644 index c33196a99ca..00000000000 --- a/tests/webTestServer.ts +++ /dev/null @@ -1,1014 +0,0 @@ -/// -// tslint:disable:no-null-keyword - -import minimist = require("minimist"); -import http = require("http"); -import fs = require("fs"); -import path = require("path"); -import url = require("url"); -import URL = url.URL; -import child_process = require("child_process"); -import os = require("os"); -import crypto = require("crypto"); -import { Readable, Writable } from "stream"; -import { isBuffer, isString, isObject } from "util"; -import { install, getErrorSource } from "source-map-support"; - -install(); - -const port = 8888; // harness.ts and webTestResults.html depend on this exact port number. -const baseUrl = new URL(`http://localhost:${port}/`); -const rootDir = path.dirname(__dirname); -const useCaseSensitiveFileNames = isFileSystemCaseSensitive(); - -const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome"; -let browser: "edge" | "chrome" | "none" = defaultBrowser; -let grep: string | undefined; -let verbose = false; - -interface FileBasedTest { - file: string; - configurations?: FileBasedTestConfiguration[]; -} - -interface FileBasedTestConfiguration { - [setting: string]: string; -} - -function isFileSystemCaseSensitive(): boolean { - // win32\win64 are case insensitive platforms - const platform = os.platform(); - if (platform === "win32" || platform === "win64") { - return false; - } - // If this file exists under a different case, we must be case-insensitve. - return !fs.existsSync(swapCase(__filename)); -} - -function swapCase(s: string): string { - return s.replace(/\w/g, (ch) => { - const up = ch.toUpperCase(); - return ch === up ? ch.toLowerCase() : up; - }); -} - -function hasLeadingSeparator(pathname: string) { - const ch = pathname.charAt(0); - return ch === "/" || ch === "\\"; -} - -function ensureLeadingSeparator(pathname: string) { - return hasLeadingSeparator(pathname) ? pathname : "/" + pathname; -} - -function trimLeadingSeparator(pathname: string) { - return hasLeadingSeparator(pathname) ? pathname.slice(1) : pathname; -} - -function normalizeSlashes(path: string) { - return path.replace(/\\+/g, "/"); -} - -function hasTrailingSeparator(pathname: string) { - const ch = pathname.charAt(pathname.length - 1); - return ch === "/" || ch === "\\"; -} - -function toServerPath(url: url.URL | string) { - if (typeof url === "string") url = new URL(url, baseUrl); - const pathname = decodeURIComponent(url.pathname); - return path.join(rootDir, pathname); -} - -function toClientPath(pathname: string) { - pathname = normalizeSlashes(pathname); - pathname = trimLeadingSeparator(pathname); - - const serverPath = path.resolve(rootDir, pathname); - if (serverPath.slice(0, rootDir.length) !== rootDir) { - return undefined; - } - - let clientPath = serverPath.slice(rootDir.length); - clientPath = ensureLeadingSeparator(clientPath); - clientPath = normalizeSlashes(clientPath); - return clientPath; -} - -function flatMap(array: T[], selector: (value: T) => U | U[]) { - let result: U[] = []; - for (const item of array) { - const mapped = selector(item); - if (Array.isArray(mapped)) { - result = result.concat(mapped); - } - else { - result.push(mapped); - } - } - return result; -} - -declare module "http" { - interface IncomingHttpHeaders { - "if-match"?: string; - "if-none-match"?: string; - "if-modified-since"?: string; - "if-unmodified-since"?: string; - "accept-charset"?: string; - "accept-encoding"?: string; - "range"?: string; - } -} - -function getQuality(value: T) { - return value.quality === undefined ? 1 : value.quality; -} - -function bestMatch(value: T, patterns: TPattern[], isMatch: (value: T, pattern: TPattern) => boolean) { - let match: TPattern | undefined; - for (const pattern of patterns) { - if (!isMatch(value, pattern)) continue; - if (match === undefined || getQuality(pattern) > getQuality(match)) { - match = pattern; - } - } - return match; -} - -const mediaTypeParser = /^([^\/]+)\/([^\/;]+)(?:;(.*))?$/; - -interface MediaType { - type: string; - subtype: string; - parameters: Record; - charset?: string; - quality?: number; -} - -function parseMediaType(mediaType: string): MediaType { - const match = mediaTypeParser.exec(mediaType); - if (!match) throw new Error("Invalid media type"); - const type = match[1].trim(); - const subtype = match[2].trim(); - if (type === "*" && subtype !== "*") throw new Error("Invalid media type"); - const parameters: Record = {}; - let charset: string | undefined; - let quality: number | undefined; - if (match[3]) { - for (const parameter of match[3].split(";")) { - const pair = parameter.split("="); - const name = pair[0].trim(); - const value = pair[1].trim(); - parameters[name] = value; - if (name === "charset") charset = value; - if (name === "q") quality = +value; - } - } - return { type, subtype, parameters, charset, quality }; -} - -function parseMediaTypes(value: string) { - const mediaTypes: MediaType[] = []; - for (const mediaRange of value.split(",")) { - mediaTypes.push(parseMediaType(mediaRange)); - } - return mediaTypes; -} - -function matchesMediaType(mediaType: MediaType, mediaTypePattern: MediaType) { - if (mediaTypePattern.type === "*") return true; - if (mediaTypePattern.type === mediaType.type) { - if (mediaTypePattern.subtype === "*") return true; - if (mediaTypePattern.subtype === mediaType.subtype) return true; - } - return false; -} - -interface StringWithQuality { - value: string; - quality?: number; -} - -const stringWithQualityParser = /^([^;]+)(;\s*q\s*=\s*([^\s]+)\s*)?$/; - -function parseStringWithQuality(value: string) { - const match = stringWithQualityParser.exec(value); - if (!match) throw new Error("Invalid header value"); - return { value: match[1].trim(), quality: match[2] ? +match[2] : undefined }; -} - -function parseStringsWithQuality(value: string) { - const charsets: StringWithQuality[] = []; - for (const charset of value.split(",")) { - charsets.push(parseStringWithQuality(charset)); - } - return charsets; -} - -function matchesCharSet(charset: string, charsetPattern: StringWithQuality) { - return charsetPattern.value === "*" || charsetPattern.value === charset; -} - -function computeETag(stats: fs.Stats) { - return JSON.stringify(crypto - .createHash("sha1") - .update(JSON.stringify({ - dev: stats.dev, - ino: stats.ino, - mtime: stats.mtimeMs, - size: stats.size - })) - .digest("base64")); -} - -function tryParseETags(value: string | undefined): "*" | string[] | undefined { - if (!value) return undefined; - if (value === "*") return value; - const etags: string[] = []; - for (const etag of value.split(",")) { - etags.push(etag.trim()); - } - return etags; -} - -function matchesETag(etag: string | undefined, condition: "*" | string[] | undefined) { - if (!condition) return true; - if (!etag) return false; - return condition === "*" || condition.indexOf(etag) >= 0; -} - -function tryParseDate(value: string | undefined) { - return value ? new Date(value) : undefined; -} - -interface ByteRange { - start: number; - end: number; -} - -const byteRangeParser = /^\s*(\d+)\s*-\s*(\d+)\s*$/; - -function tryParseByteRange(value: string, contentLength: number): ByteRange | undefined { - const match = byteRangeParser.exec(value); - const firstBytePos = match && match[1] ? +match[1] : undefined; - const lastBytePos = match && match[2] ? +match[2] : undefined; - if (firstBytePos !== undefined && lastBytePos !== undefined) { - if (lastBytePos < firstBytePos) return undefined; - return { start: firstBytePos, end: lastBytePos + 1 }; - } - if (firstBytePos !== undefined) return { start: firstBytePos, end: contentLength }; - if (lastBytePos !== undefined) return { start: contentLength - lastBytePos, end: contentLength }; - return undefined; -} - -function tryParseByteRanges(value: string, contentLength: number): ByteRange[] | undefined { - if (!value.startsWith("bytes=")) return; - const ranges: ByteRange[] = []; - for (const range of value.slice(6).split(",")) { - const byteRange = tryParseByteRange(range, contentLength); - if (byteRange === undefined) return undefined; - if (byteRange.start >= contentLength) continue; - ranges.push(byteRange); - } - return ranges; -} - -function once void>(callback: T): T; -function once(callback: (...args: any[]) => void) { - let called = false; - return (...args: any[]) => { - if (called) return; - called = true; - callback(...args); - }; -} - -function mkdirp(dirname: string, callback: (err: NodeJS.ErrnoException | null) => void) { - fs.mkdir(dirname, err => { - if (err && err.code === "EEXIST") err = null; - if (err && err.code === "ENOENT") { - const parentdir = path.dirname(dirname); - if (!parentdir || parentdir === dirname) return callback(err); - return mkdirp(parentdir, err => { - if (err) return callback(err); - return fs.mkdir(dirname, callback); - }); - } - return callback(err); - }); -} - -function getAccessibleFileSystemEntries(pathname: string) { - try { - const entries = fs.readdirSync(pathname).sort(); - const files: string[] = []; - const directories: string[] = []; - for (const entry of entries) { - // This is necessary because on some file system node fails to exclude - // "." and "..". See https://github.com/nodejs/node/issues/4002 - if (entry === "." || entry === "..") { - continue; - } - const name = path.join(pathname, entry); - - let stat: fs.Stats; - try { - stat = fs.statSync(name); - } - catch (e) { - continue; - } - - if (stat.isFile()) { - files.push(entry); - } - else if (stat.isDirectory()) { - directories.push(entry); - } - } - return { files, directories }; - } - catch (e) { - return { files: [], directories: [] }; - } -} - -function guessMediaType(pathname: string) { - switch (path.extname(pathname).toLowerCase()) { - case ".html": return "text/html; charset=utf-8"; - case ".css": return "text/css; charset=utf-8"; - case ".js": return "application/javascript; charset=utf-8"; - case ".mjs": return "application/javascript; charset=utf-8"; - case ".jsx": return "text/jsx; charset=utf-8"; - case ".ts": return "text/plain; charset=utf-8"; - case ".tsx": return "text/plain; charset=utf-8"; - case ".json": return "text/plain; charset=utf-8"; - case ".map": return "application/json; charset=utf-8"; - default: return "application/octet-stream"; - } -} - -function readContent(req: http.ServerRequest, callback: (err: NodeJS.ErrnoException | null, content: string | null) => void) { - const chunks: Buffer[] = []; - const done = once((err: NodeJS.ErrnoException | null) => { - if (err) return callback(err, /*content*/ null); - let content: string | null = null; - try { - content = Buffer.concat(chunks).toString("utf8"); - } - catch (e) { - err = e; - } - return callback(err, content); - }); - req.on("data", chunk => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf8"))); - req.on("error", err => done(err)); - req.on("end", () => done(/*err*/ null)); -} - -function saveToFile(file: string, readable: Readable, callback: (err: NodeJS.ErrnoException | null) => void) { - callback = once(callback); - const writable = fs.createWriteStream(file, { autoClose: true }); - writable.on("error", err => callback(err)); - readable.on("end", () => callback(/*err*/ null)); - readable.pipe(writable, { end: true }); -} - -function sendContent(res: http.ServerResponse, statusCode: number, content: string | Buffer, contentType: string): void; -function sendContent(res: http.ServerResponse, statusCode: number, content: Readable, contentType: string, contentLength: number): void; -function sendContent(res: http.ServerResponse, statusCode: number, content: string | Buffer | Readable, contentType: string, contentLength?: number) { - res.statusCode = statusCode; - res.setHeader("Content-Type", contentType); - if (isString(content)) { - res.setHeader("Content-Length", Buffer.byteLength(content, "utf8")); - res.end(content, "utf8"); - } - else if (isBuffer(content)) { - res.setHeader("Content-Length", content.byteLength); - res.end(content); - } - else { - if (contentLength !== undefined) res.setHeader("Content-Length", contentLength); - content.on("error", e => sendInternalServerError(res, e)); - content.pipe(res, { end: true }); - } -} - -function sendJson(res: http.ServerResponse, statusCode: number, value: any) { - try { - sendContent(res, statusCode, JSON.stringify(value), "application/json; charset=utf-8"); - } - catch (e) { - sendInternalServerError(res, e); - } -} - -function sendCreated(res: http.ServerResponse, location?: string, etag?: string) { - res.statusCode = 201; - if (location) res.setHeader("Location", location); - if (etag) res.setHeader("ETag", etag); - res.end(); -} - -function sendNoContent(res: http.ServerResponse) { - res.statusCode = 204; - res.end(); -} - -function sendFound(res: http.ServerResponse, location: string) { - res.statusCode = 302; - res.setHeader("Location", location); - res.end(); -} - -function sendNotModified(res: http.ServerResponse) { - res.statusCode = 304; - res.end(); -} - -function sendBadRequest(res: http.ServerResponse) { - res.statusCode = 400; - res.end(); -} - -function sendNotFound(res: http.ServerResponse) { - res.statusCode = 404; - res.end(); -} - -function sendMethodNotAllowed(res: http.ServerResponse, allowedMethods: string[]) { - res.statusCode = 405; - res.setHeader("Allow", allowedMethods); - res.end(); -} - -function sendNotAcceptable(res: http.ServerResponse) { - res.statusCode = 406; - res.end(); -} - -function sendPreconditionFailed(res: http.ServerResponse) { - res.statusCode = 412; - res.end(); -} - -function sendUnsupportedMediaType(res: http.ServerResponse) { - res.statusCode = 415; - res.end(); -} - -function sendRangeNotSatisfiable(res: http.ServerResponse) { - res.statusCode = 416; - res.end(); -} - -function sendInternalServerError(res: http.ServerResponse, error: Error) { - console.error(error); - return sendContent(res, /*statusCode*/ 500, error.stack, "text/plain; charset=utf8"); -} - -function sendNotImplemented(res: http.ServerResponse) { - res.statusCode = 501; - res.end(); -} - -function shouldIgnoreCache(url: URL) { - switch (url.pathname) { - case "/built/local/bundle.js": - case "/built/local/bundle.js.map": - return true; - default: - return false; - } -} - -function isAcceptable(req: http.ServerRequest, contentType: string) { - const mediaType = parseMediaType(contentType); - return isAcceptableMediaType(req, mediaType) - && isAcceptableCharSet(req, mediaType) - && isAcceptableEncoding(req); -} - -function isAcceptableMediaType(req: http.ServerRequest, mediaType: MediaType) { - if (!req.headers.accept) return true; - const acceptedMediaType = bestMatch(mediaType, parseMediaTypes(req.headers.accept), matchesMediaType); - return acceptedMediaType ? getQuality(acceptedMediaType) > 0 : false; -} - -function isAcceptableCharSet(req: http.ServerRequest, mediaType: MediaType) { - if (!req.headers["accept-charset"]) return true; - const acceptedCharSet = bestMatch(mediaType.charset || "utf-8", parseStringsWithQuality(req.headers["accept-charset"]), matchesCharSet); - return acceptedCharSet ? getQuality(acceptedCharSet) > 0 : false; -} - -function isAcceptableEncoding(req: http.ServerRequest) { - if (!req.headers["accept-encoding"]) return true; - const acceptedEncoding = bestMatch(/*value*/ undefined, parseStringsWithQuality(req.headers["accept-encoding"]), (_, pattern) => pattern.value === "*" || pattern.value === "identity"); - return acceptedEncoding ? getQuality(acceptedEncoding) > 0 : true; -} - -function shouldSendNotModified(req: http.ServerRequest, stats: fs.Stats, etag: string) { - const ifNoneMatch = tryParseETags(req.headers["if-none-match"]); - if (ifNoneMatch) return matchesETag(etag, ifNoneMatch); - - const ifModifiedSince = tryParseDate(req.headers["if-modified-since"]); - if (ifModifiedSince) return stats.mtime.getTime() <= ifModifiedSince.getTime(); - - return false; -} - -function shouldSendPreconditionFailed(req: http.ServerRequest, stats: fs.Stats, etag: string) { - const ifMatch = tryParseETags(req.headers["if-match"]); - if (ifMatch && !matchesETag(etag, ifMatch)) return true; - - const ifUnmodifiedSince = tryParseDate(req.headers["if-unmodified-since"]); - if (ifUnmodifiedSince && stats.mtime.getTime() > ifUnmodifiedSince.getTime()) return true; - - return false; -} - -function handleGetRequest(req: http.ServerRequest, res: http.ServerResponse) { - const url = new URL(req.url, baseUrl); - if (url.pathname === "/") { - url.pathname = "/tests/webTestResults.html"; - return sendFound(res, url.toString()); - } - - const file = toServerPath(url); - fs.stat(file, (err, stats) => { - try { - if (err) { - if (err.code === "ENOENT") return sendNotFound(res); - return sendInternalServerError(res, err); - } - if (stats && stats.isFile()) { - const contentType = guessMediaType(file); - if (!isAcceptable(req, contentType)) return sendNotAcceptable(res); - - const etag = computeETag(stats); - if (shouldSendNotModified(req, stats, etag)) return sendNotModified(res); - if (shouldSendPreconditionFailed(req, stats, etag)) return sendPreconditionFailed(res); - - if (shouldIgnoreCache(url)) res.setHeader("Cache-Control", "no-store"); - res.setHeader("Last-Modified", stats.mtime.toUTCString()); - res.setHeader("ETag", etag); - res.setHeader("Content-Type", contentType); - res.setHeader("Accept-Ranges", "bytes"); - - const ranges = req.headers.range && tryParseByteRanges(req.headers.range, stats.size); - if (ranges && ranges.length === 0) return sendRangeNotSatisfiable(res); - - let start: number | undefined; - let end: number | undefined; - if (ranges && ranges.length === 1) { - start = ranges[0].start; - end = ranges[0].end; - if (start >= stats.size || end > stats.size) return sendRangeNotSatisfiable(res); - res.statusCode = 206; - res.setHeader("Content-Length", end - start); - res.setHeader("Content-Range", `bytes ${start}-${end - 1}/${stats.size}`); - } - else { - res.statusCode = 200; - res.setHeader("Content-Length", stats.size); - } - if (req.method === "HEAD") return res.end(); - const readable = fs.createReadStream(file, { start, end, autoClose: true }); - readable.on("error", err => sendInternalServerError(res, err)); - readable.pipe(res, { end: true }); - } - else { - if (req.headers["if-match"] === "*") return sendPreconditionFailed(res); - return sendNotFound(res); - } - } - catch (e) { - return sendInternalServerError(res, e); - } - }); -} - -function handlePutRequest(req: http.ServerRequest, res: http.ServerResponse) { - if (req.headers["content-encoding"]) return sendUnsupportedMediaType(res); - if (req.headers["content-range"]) return sendNotImplemented(res); - const file = toServerPath(req.url); - fs.stat(file, (err, stats) => { - try { - if (err && err.code !== "ENOENT") return sendInternalServerError(res, err); - if (stats && !stats.isFile()) return sendMethodNotAllowed(res, []); - return mkdirp(path.dirname(file), err => { - if (err) return sendInternalServerError(res, err); - try { - const writable = fs.createWriteStream(file, { autoClose: true }); - writable.on("error", err => sendInternalServerError(res, err)); - writable.on("finish", () => { - if (stats) return sendNoContent(res); - fs.stat(file, (err, stats) => { - if (err) return sendInternalServerError(res, err); - return sendCreated(res, toClientPath(file), computeETag(stats)); - }); - }); - req.pipe(writable, { end: true }); - return; - } - catch (e) { - return sendInternalServerError(res, e); - } - }); - } - catch (e) { - return sendInternalServerError(res, e); - } - }); -} - -function handleDeleteRequest(req: http.ServerRequest, res: http.ServerResponse) { - const file = toServerPath(req.url); - fs.stat(file, (err, stats) => { - try { - if (err && err.code !== "ENOENT") return sendInternalServerError(res, err); - if (!stats) return sendNotFound(res); - if (stats.isFile()) return fs.unlink(file, handleResult); - if (stats.isDirectory()) return fs.rmdir(file, handleResult); - return sendNotFound(res); - function handleResult(err: NodeJS.ErrnoException) { - if (err && err.code !== "ENOENT") return sendInternalServerError(res, err); - if (err) return sendNotFound(res); - return sendNoContent(res); - } - } - catch (e) { - return sendInternalServerError(res, e); - } - }); -} - -function handleOptionsRequest(req: http.ServerRequest, res: http.ServerResponse) { - res.setHeader("X-Case-Sensitivity", useCaseSensitiveFileNames ? "CS" : "CI"); - return sendNoContent(res); -} - -function handleApiResolve(req: http.ServerRequest, res: http.ServerResponse) { - readContent(req, (err, content) => { - try { - if (err) return sendInternalServerError(res, err); - if (!content) return sendBadRequest(res); - const serverPath = toServerPath(content); - const clientPath = toClientPath(serverPath); - if (clientPath === undefined) return sendBadRequest(res); - return sendContent(res, /*statusCode*/ 200, clientPath, /*contentType*/ "text/plain;charset=utf-8"); - } - catch (e) { - return sendInternalServerError(res, e); - } - }); -} - -function handleApiEnumerateTestFiles(req: http.ServerRequest, res: http.ServerResponse) { - readContent(req, (err, content) => { - try { - if (err) return sendInternalServerError(res, err); - if (!content) return sendBadRequest(res); - const tests: (string | FileBasedTest)[] = enumerateTestFiles(content); - return sendJson(res, /*statusCode*/ 200, tests); - } - catch (e) { - return sendInternalServerError(res, e); - } - }); -} - -function enumerateTestFiles(runner: string) { - switch (runner) { - case "conformance": - case "compiler": - return listFiles(`tests/cases/${runner}`, /*serverDirname*/ undefined, /\.tsx?$/, { recursive: true }).map(parseCompilerTestConfigurations); - case "fourslash": - return listFiles(`tests/cases/fourslash`, /*serverDirname*/ undefined, /\.ts/i, { recursive: false }); - case "fourslash-shims": - return listFiles(`tests/cases/fourslash/shims`, /*serverDirname*/ undefined, /\.ts/i, { recursive: false }); - case "fourslash-shims-pp": - return listFiles(`tests/cases/fourslash/shims-pp`, /*serverDirname*/ undefined, /\.ts/i, { recursive: false }); - case "fourslash-server": - return listFiles(`tests/cases/fourslash/server`, /*serverDirname*/ undefined, /\.ts/i, { recursive: false }); - default: - throw new Error(`Runner '${runner}' not supported in browser tests.`); - } -} - -// Regex for parsing options in the format "@Alpha: Value of any sort" -const optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*([^\r\n]*)/gm; // multiple matches on multiple lines - -function extractCompilerSettings(content: string): Record { - const opts: Record = {}; - - let match: RegExpExecArray; - while ((match = optionRegex.exec(content)) !== null) { - opts[match[1]] = match[2].trim(); - } - - return opts; -} - -function splitVaryBySettingValue(text: string): string[] | undefined { - if (!text) return undefined; - const entries = text.split(/,/).map(s => s.trim().toLowerCase()).filter(s => s.length > 0); - return entries && entries.length > 1 ? entries : undefined; -} - -function computeFileBasedTestConfigurationVariations(configurations: FileBasedTestConfiguration[], variationState: FileBasedTestConfiguration, varyByEntries: [string, string[]][], offset: number) { - if (offset >= varyByEntries.length) { - // make a copy of the current variation state - configurations.push({ ...variationState }); - return; - } - - const [varyBy, entries] = varyByEntries[offset]; - for (const entry of entries) { - // set or overwrite the variation - variationState[varyBy] = entry; - computeFileBasedTestConfigurationVariations(configurations, variationState, varyByEntries, offset + 1); - } -} - -function getFileBasedTestConfigurations(settings: Record, varyBy: string[]): FileBasedTestConfiguration[] | undefined { - let varyByEntries: [string, string[]][] | undefined; - for (const varyByKey of varyBy) { - if (Object.prototype.hasOwnProperty.call(settings, varyByKey)) { - const entries = splitVaryBySettingValue(settings[varyByKey]); - if (entries) { - if (!varyByEntries) varyByEntries = []; - varyByEntries.push([varyByKey, entries]); - } - } - } - - if (!varyByEntries) return undefined; - - const configurations: FileBasedTestConfiguration[] = []; - computeFileBasedTestConfigurationVariations(configurations, {}, varyByEntries, 0); - return configurations; -} - -function parseCompilerTestConfigurations(file: string): FileBasedTest { - const content = fs.readFileSync(path.join(rootDir, file), "utf8"); - const settings = extractCompilerSettings(content); - const configurations = getFileBasedTestConfigurations(settings, ["module", "target"]); - return { file, configurations }; -} - -function handleApiListFiles(req: http.ServerRequest, res: http.ServerResponse) { - readContent(req, (err, content) => { - try { - if (err) return sendInternalServerError(res, err); - if (!content) return sendBadRequest(res); - const serverPath = toServerPath(content); - const files = listFiles(content, serverPath, /*spec*/ undefined, { recursive: true }); - return sendJson(res, /*statusCode*/ 200, files); - } - catch (e) { - return sendInternalServerError(res, e); - } - }); -} - -function listFiles(clientDirname: string, serverDirname: string = path.resolve(rootDir, clientDirname), spec?: RegExp, options: { recursive?: boolean } = {}): string[] { - const files: string[] = []; - visit(serverDirname, clientDirname, files); - return files; - - function visit(dirname: string, relative: string, results: string[]) { - const { files, directories } = getAccessibleFileSystemEntries(dirname); - for (const file of files) { - if (!spec || file.match(spec)) { - results.push(path.join(relative, file)); - } - } - for (const directory of directories) { - if (options.recursive) { - visit(path.join(dirname, directory), path.join(relative, directory), results); - } - } - } -} - -function handleApiDirectoryExists(req: http.ServerRequest, res: http.ServerResponse) { - readContent(req, (err, content) => { - try { - if (err) return sendInternalServerError(res, err); - if (!content) return sendBadRequest(res); - const serverPath = toServerPath(content); - fs.stat(serverPath, (err, stats) => { - try { - if (err && err.code !== "ENOENT") return sendInternalServerError(res, err); - return sendJson(res, /*statusCode*/ 200, !!stats && stats.isDirectory()); - } - catch (e) { - return sendInternalServerError(res, e); - } - }); - } - catch (e) { - return sendInternalServerError(res, e); - } - }); -} - -function handleApiGetAccessibleFileSystemEntries(req: http.ServerRequest, res: http.ServerResponse) { - readContent(req, (err, content) => { - try { - if (err) return sendInternalServerError(res, err); - if (!content) return sendBadRequest(res); - const serverPath = toServerPath(content); - return sendJson(res, /*statusCode*/ 200, getAccessibleFileSystemEntries(serverPath)); - } - catch (e) { - return sendInternalServerError(res, e); - } - }); -} - -function handlePostRequest(req: http.ServerRequest, res: http.ServerResponse) { - // API responses should not be cached - res.setHeader("Cache-Control", "no-cache"); - switch (new URL(req.url, baseUrl).pathname) { - case "/api/resolve": return handleApiResolve(req, res); - case "/api/listFiles": return handleApiListFiles(req, res); - case "/api/enumerateTestFiles": return handleApiEnumerateTestFiles(req, res); - case "/api/directoryExists": return handleApiDirectoryExists(req, res); - case "/api/getAccessibleFileSystemEntries": return handleApiGetAccessibleFileSystemEntries(req, res); - default: return sendMethodNotAllowed(res, ["HEAD", "GET", "PUT", "DELETE", "OPTIONS"]); - } -} - -function handleRequest(req: http.ServerRequest, res: http.ServerResponse) { - try { - switch (req.method) { - case "HEAD": - case "GET": return handleGetRequest(req, res); - case "PUT": return handlePutRequest(req, res); - case "POST": return handlePostRequest(req, res); - case "DELETE": return handleDeleteRequest(req, res); - case "OPTIONS": return handleOptionsRequest(req, res); - default: return sendMethodNotAllowed(res, ["HEAD", "GET", "PUT", "POST", "DELETE"]); - } - } - catch (e) { - return sendInternalServerError(res, e); - } -} - -function startServer() { - console.log(`Static file server running at\n => http://localhost:${port}/\nCTRL + C to shutdown`); - return http.createServer(handleRequest).listen(port); -} - -const REG_COLUMN_PADDING = 4; - -function queryRegistryValue(keyPath: string, callback: (error: Error | null, value: string) => void) { - const args = ["query", keyPath]; - child_process.execFile("reg", ["query", keyPath, "/ve"], { encoding: "utf8" }, (error, stdout) => { - if (error) return callback(error, null); - - const valueLine = stdout.replace(/^\r\n.+?\r\n|\r\n\r\n$/g, ""); - if (!valueLine) { - return callback(new Error("Unable to retrieve value."), null); - } - - const valueNameColumnOffset = REG_COLUMN_PADDING; - if (valueLine.lastIndexOf("(Default)", valueNameColumnOffset) !== valueNameColumnOffset) { - return callback(new Error("Unable to retrieve value."), null); - } - - const dataTypeColumnOffset = valueNameColumnOffset + "(Default)".length + REG_COLUMN_PADDING; - if (valueLine.lastIndexOf("REG_SZ", dataTypeColumnOffset) !== dataTypeColumnOffset) { - return callback(new Error("Unable to retrieve value."), null); - } - - const valueColumnOffset = dataTypeColumnOffset + "REG_SZ".length + REG_COLUMN_PADDING; - const value = valueLine.slice(valueColumnOffset); - return callback(null, value); - }); -} - -interface Browser { - description: string; - command: string; -} - -function createBrowserFromPath(path: string): Browser { - return { description: path, command: path }; -} - -function getChromePath(callback: (error: Error | null, browser: Browser | string | null) => void) { - switch (os.platform()) { - case "win32": - return queryRegistryValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", (error, value) => { - if (error) return callback(null, "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"); - return callback(null, createBrowserFromPath(value)); - }); - case "darwin": return callback(null, "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"); - case "linux": return callback(null, "/opt/google/chrome/chrome"); - default: return callback(new Error(`Chrome location is unknown for platform '${os.platform()}'`), null); - } -} - -function getEdgePath(callback: (error: Error | null, browser: Browser | null) => void) { - switch (os.platform()) { - case "win32": return callback(null, { description: "Microsoft Edge", command: "cmd /c start microsoft-edge:%1" }); - default: return callback(new Error(`Edge location is unknown for platform '${os.platform()}'`), null); - } -} - -function getBrowserPath(callback: (error: Error | null, browser: Browser | null) => void) { - switch (browser) { - case "chrome": return getChromePath(afterGetBrowserPath); - case "edge": return getEdgePath(afterGetBrowserPath); - default: return callback(new Error(`Browser location is unknown for '${browser}'`), null); - } - - function afterGetBrowserPath(error: Error | null, browser: Browser | string | null) { - if (error) return callback(error, null); - if (typeof browser === "object") return callback(null, browser); - return fs.stat(browser, (error, stats) => { - if (!error && stats.isFile()) { - return callback(null, createBrowserFromPath(browser)); - } - if (browser === "chrome") return callback(null, createBrowserFromPath("chrome")); - return callback(new Error(`Browser location is unknown for '${browser}'`), null); - }); - } -} - -function startClient(server: http.Server) { - let browserPath: string; - if (browser === "none") { - return; - } - - getBrowserPath((error, browser) => { - if (error) return console.error(error); - console.log(`Using browser: ${browser.description}`); - const queryString = grep ? `?grep=${grep}` : ""; - const args = [`http://localhost:${port}/tests/webTestResults.html${queryString}`]; - if (browser.command.indexOf("%") === -1) { - child_process.spawn(browser.command, args); - } - else { - const command = browser.command.replace(/%(\d+)/g, (_, offset) => args[+offset - 1]); - child_process.exec(command); - } - }); -} - -function printHelp() { - console.log("Runs an http server on port 8888, looking for tests folder in the current directory\n"); - console.log("Syntax: node webTestServer.js [browser] [tests] [--verbose]\n"); - console.log("Options:"); - console.log(" The browser to launch. One of 'edge', 'chrome', or 'none' (default 'edge' on Windows, otherwise `chrome`)."); - console.log(" A regular expression to pass to Mocha."); - console.log(" --verbose Enables verbose logging."); -} - -function parseCommandLine(args: string[]) { - const parsed = minimist(args, { boolean: ["help", "verbose"] }); - if (parsed.help) { - printHelp(); - return false; - } - - if (parsed.verbose) { - verbose = true; - } - - const [parsedBrowser = defaultBrowser, parsedGrep, ...unrecognized] = parsed._; - if (parsedBrowser !== "edge" && parsedBrowser !== "chrome" && parsedBrowser !== "none") { - console.log(`Unrecognized browser '${parsedBrowser}', expected 'edge', 'chrome', or 'none'.`); - return false; - } - - if (unrecognized.length > 0) { - console.log(`Unrecognized argument: ${unrecognized[0]}`); - return false; - } - - browser = parsedBrowser; - grep = parsedGrep; - return true; -} - -function log(msg: string) { - if (verbose) { - console.log(msg); - } -} - -function main() { - if (parseCommandLine(process.argv.slice(2))) { - startClient(startServer()); - } -} - -main(); -// tslint:enable:no-null-keyword \ No newline at end of file diff --git a/tests/webTestServer.tsconfig.json b/tests/webTestServer.tsconfig.json deleted file mode 100644 index ff9fbb54bb5..00000000000 --- a/tests/webTestServer.tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "module": "commonjs", - "declaration": false, - "removeComments": true, - "noResolve": false, - "stripInternal": false, - "sourceMap": true - }, - "files": [ - "webTestServer.ts" - ] -} diff --git a/tests/webhost/favicon-32x32.png b/tests/webhost/favicon-32x32.png deleted file mode 100644 index efbce163f929bbd4d551f93940a6d9187f9cca1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmV+*1K<3KP)$rXRb0CKM@VaABD)^?)d-z|EA9A8G*;C(CYjB{$Q!t z8iL2X+V9ut_0{P0*y{E6`u*zk`qt?6+3NO;z~h9x;r{;r-|hFi+U~vE@7wJ5`uzT3 zsn`Q`!W4hT7J$YghRMt0@`=CV6Mn|bKV!{qSy`=ib2LYUDAcf%ux$;008 z@%a3r%;+eI${~iym&fMp^!h1^%LR7AHj&Pg#O3Pq`k2S&0(HUzbiqTI(c0_w)93UR zfX0Ts;ot7}7=g#E((7ic+9im}x!LZ{v0(HR;e8mxc#Rhl724&}%0003)NklOo0CsnG7ns=H{%tS7qm0LUFMO_cW}n%acXx^Y z`E#&PVIm@C=I?!--tbA3kyu6>WArB7m%_1TOI(=v1Z!eaa!RT#&7Lk)!I1%#%&hF3 zP?alHAuqq6u&5X)DJ(53uK+5ms+~2pb*e!1E=6f@1C33JVrT}cTRdK0Yn%9%f - - WebTSC - - - - - - - -
- -
-
-

Command line:

- -

Output:

- -
-
- -
-
-
- - \ No newline at end of file diff --git a/tests/webhost/webtsc.ts b/tests/webhost/webtsc.ts deleted file mode 100644 index 3c682df6274..00000000000 --- a/tests/webhost/webtsc.ts +++ /dev/null @@ -1,104 +0,0 @@ -/// - -namespace TypeScript.WebTsc { - - declare var RealActiveXObject: { new (s: string): any }; - - function getWScriptSystem() { - const fso = new RealActiveXObject("Scripting.FileSystemObject"); - - const fileStream = new ActiveXObject("ADODB.Stream"); - fileStream.Type = 2 /*text*/; - - const args: string[] = []; - for (let i = 0; i < WScript.Arguments.length; i++) { - args[i] = WScript.Arguments.Item(i); - } - return { - args: args, - newLine: "\r\n", - write(s: string): void { - WScript.StdOut.Write(s); - }, - writeErr(s: string): void { - WScript.StdErr.Write(s); - }, - readFile(fileName: string, encoding?: string): string { - if (!fso.FileExists(fileName)) { - return undefined; - } - fileStream.Open(); - try { - if (encoding) { - fileStream.Charset = encoding; - fileStream.LoadFromFile(fileName); - } - else { - // Load file and read the first two bytes into a string with no interpretation - fileStream.Charset = "x-ansi"; - fileStream.LoadFromFile(fileName); - const bom = fileStream.ReadText(2) || ""; - // Position must be at 0 before encoding can be changed - fileStream.Position = 0; - // [0xFF,0xFE] and [0xFE,0xFF] mean utf-16 (little or big endian), otherwise default to utf-8 - fileStream.Charset = bom.length >= 2 && (bom.charCodeAt(0) === 0xFF && bom.charCodeAt(1) === 0xFE || bom.charCodeAt(0) === 0xFE && bom.charCodeAt(1) === 0xFF) ? "unicode" : "utf-8"; - } - // ReadText method always strips byte order mark from resulting string - return fileStream.ReadText(); - } - catch (e) { - throw e; - } - finally { - fileStream.Close(); - } - }, - writeFile(fileName: string, data: string): boolean { - const f = fso.CreateTextFile(fileName, true); - f.Write(data); - f.Close(); - return true; - }, - resolvePath(path: string): string { - return fso.GetAbsolutePathName(path); - }, - fileExists(path: string): boolean { - return fso.FileExists(path); - }, - directoryExists(path: string) { - return fso.FolderExists(path); - }, - createDirectory(directoryName: string) { - if (!this.directoryExists(directoryName)) { - fso.CreateFolder(directoryName); - } - }, - getExecutingFilePath() { - return WScript.ScriptFullName; - }, - getCurrentDirectory() { - return ""; - }, - getMemoryUsage() { - return 0; - }, - exit(exitCode?: number): void { - WScript.Quit(exitCode); - }, - useCaseSensitiveFileNames: false - }; - } - - export function prepareCompiler(currentDir: string, stdOut: ITextWriter, stdErr: ITextWriter) { - const shell = new RealActiveXObject("WScript.Shell"); - shell.CurrentDirectory = currentDir; - WScript.ScriptFullName = currentDir + "\\tc.js"; - WScript.StdOut = stdOut; - WScript.StdErr = stdErr; - sys = getWScriptSystem(); - - return (commandLine: string) => { - ts.executeCommandLine(commandLine.split(" ")); - }; - } -} diff --git a/tests/webhost/webtsc.tsconfig.json b/tests/webhost/webtsc.tsconfig.json deleted file mode 100644 index a30b4ceef83..00000000000 --- a/tests/webhost/webtsc.tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "module": "commonjs", - "declaration": false, - "removeComments": true, - "noResolve": false, - "stripInternal": false, - "sourceMap": true - }, - "files": [ - "webtsc.ts" - ] -} \ No newline at end of file diff --git a/tests/webhost/wscript.js b/tests/webhost/wscript.js deleted file mode 100644 index 8cacfd4eb7d..00000000000 --- a/tests/webhost/wscript.js +++ /dev/null @@ -1,22 +0,0 @@ -var RealActiveXObject = ActiveXObject - -var WScript; -(function (WScript) { - WScript.Arguments = []; - WScript.Echo = function (str) { }; - WScript.StdErr = { - Write: function() {} - } - WScript.StdOut = { - Write: function () { }, - WriteLine: function () { } - } - - WScript.Quit = function (exitCode) { } -})(WScript || (WScript = {})); - -var ActiveXObject = (function () { - function ActiveXObject(name) { - } - return ActiveXObject; -})();