From 7cd77536ac25fd2a4358a4ce4ba1494f1767c6e7 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 17 Nov 2016 18:16:25 -0800 Subject: [PATCH] Initial parse, check, emit for partial application, pipeline, and operator expressions --- src/compiler/binder.ts | 15 + src/compiler/checker.ts | 382 +++++++++++++++++++-- src/compiler/emitter.ts | 19 +- src/compiler/factory.ts | 23 +- src/compiler/parser.ts | 126 ++++++- src/compiler/scanner.ts | 6 + src/compiler/transformers/es2015.ts | 4 +- src/compiler/transformers/esnext.ts | 165 ++++++++- src/compiler/transformers/module/system.ts | 2 +- src/compiler/transformers/ts.ts | 2 +- src/compiler/types.ts | 37 +- src/compiler/utilities.ts | 49 ++- 12 files changed, 753 insertions(+), 77 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1afe51db021..8855755377e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3110,6 +3110,10 @@ namespace ts { let excludeFlags = TransformFlags.NodeExcludes; switch (kind) { + case SyntaxKind.BarGreaterThanToken: + transformFlags |= TransformFlags.AssertESNext; + break; + case SyntaxKind.AsyncKeyword: case SyntaxKind.AwaitExpression: // async/await is ES2017 syntax @@ -3221,12 +3225,23 @@ namespace ts { case SyntaxKind.SpreadElement: transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsSpread; + if ((node).expression.kind === SyntaxKind.OmittedExpression) { + transformFlags |= TransformFlags.AssertESNext; + } break; case SyntaxKind.SpreadAssignment: transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectSpread; break; + case SyntaxKind.PositionalElement: + transformFlags |= TransformFlags.AssertESNext; + break; + + case SyntaxKind.OperatorExpression: + transformFlags |= TransformFlags.AssertESNext; + break; + case SyntaxKind.SuperKeyword: // This node is ES6 syntax. transformFlags |= TransformFlags.AssertES2015; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d25fc8bbbb..72fc37324c2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -163,6 +163,7 @@ namespace ts { const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); + const resolvingPartialSignatures: Signature[] = []; const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); @@ -240,6 +241,7 @@ namespace ts { const visitedFlowTypes: FlowType[] = []; const potentialThisCollisions: Node[] = []; const awaitedTypeStack: number[] = []; + const operatorExpressionTypes = createMap(); const diagnostics = createDiagnosticCollection(); @@ -2616,16 +2618,16 @@ namespace ts { function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { const parameterNode = p.valueDeclaration; - if (isRestParameter(parameterNode)) { + if ((isTransient(p) && p.transientSymbolIsRest) || isRestParameter(parameterNode)) { writePunctuation(writer, SyntaxKind.DotDotDotToken); } - if (isBindingPattern(parameterNode.name)) { + if (parameterNode && isBindingPattern(parameterNode.name)) { buildBindingPatternDisplay(parameterNode.name, writer, enclosingDeclaration, flags, symbolStack); } else { appendSymbolNameOnly(p, writer); } - if (isOptionalParameter(parameterNode)) { + if (parameterNode && isOptionalParameter(parameterNode)) { writePunctuation(writer, SyntaxKind.QuestionToken); } writePunctuation(writer, SyntaxKind.ColonToken); @@ -4277,10 +4279,15 @@ namespace ts { resolveObjectTypeMembers(type, source, typeParameters, typeArguments); } + function createSignature(isConstruct: boolean, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], + resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature; function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], + resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature; + function createSignature(declaration: boolean | SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature { const sig = new Signature(checker); - sig.declaration = declaration; + sig.declaration = typeof declaration === "boolean" ? undefined : declaration; + sig.isConstruct = typeof declaration === "boolean" ? declaration : declaration && (declaration.kind === SyntaxKind.Constructor || declaration.kind === SyntaxKind.ConstructSignature); sig.typeParameters = typeParameters; sig.parameters = parameters; sig.thisParameter = thisParameter; @@ -5174,12 +5181,11 @@ namespace ts { // object type literal or interface (using the new keyword). Each way of declaring a constructor // will result in a different declaration kind. if (!signature.isolatedSignatureType) { - const isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature; const type = createObjectType(ObjectFlags.Anonymous); type.members = emptySymbols; type.properties = emptyArray; - type.callSignatures = !isConstructor ? [signature] : emptyArray; - type.constructSignatures = isConstructor ? [signature] : emptyArray; + type.callSignatures = !signature.isConstruct ? [signature] : emptyArray; + type.constructSignatures = signature.isConstruct ? [signature] : emptyArray; signature.isolatedSignatureType = type; } @@ -8119,6 +8125,10 @@ namespace ts { getSignaturesOfType(type, SignatureKind.Construct).length === 0; } + function isTransient(symbol: Symbol): symbol is TransientSymbol { + return (symbol.flags & SymbolFlags.Transient) !== 0; + } + function createTransientSymbol(source: Symbol, type: Type) { const symbol = createSymbol(source.flags | SymbolFlags.Transient, source.name); symbol.declarations = source.declarations; @@ -12307,6 +12317,11 @@ namespace ts { typeArguments = undefined; argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature); } + else if (node.kind === SyntaxKind.BinaryExpression) { + typeArguments = undefined; + argCount = getEffectiveArgumentCount(node, args, signature); + spreadArgIndex = getSpreadArgumentIndex(args); + } else { const callExpression = node; if (!callExpression.arguments) { @@ -12558,6 +12573,23 @@ namespace ts { // `getEffectiveArgumentCount` and `getEffectiveArgumentType` below. return undefined; } + else if (node.kind === SyntaxKind.BinaryExpression) { + let expression = (node).left; + if (expression.kind === SyntaxKind.ParenthesizedExpression) { + // comma expressions are right-deep + args = []; + expression = (expression).expression; + while (expression.kind === SyntaxKind.BinaryExpression && + (expression).operatorToken.kind === SyntaxKind.CommaToken) { + args.push((expression).left); + expression = (expression).right; + } + args.push(expression); + } + else { + args = [expression]; + } + } else { args = (node).arguments || emptyArray; } @@ -12837,13 +12869,17 @@ namespace ts { } } - function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], headMessage?: DiagnosticMessage): Signature { + function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], partialApplication: false, headMessage?: DiagnosticMessage): Signature; + function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], partialApplication: true, headMessage?: DiagnosticMessage): Signature[]; + function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], partialApplication: boolean, headMessage?: DiagnosticMessage): Signature | Signature[]; + function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], partialApplication: boolean, headMessage?: DiagnosticMessage): Signature | Signature[] { const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression; const isDecorator = node.kind === SyntaxKind.Decorator; + const isPipeline = node.kind === SyntaxKind.BinaryExpression; let typeArguments: TypeNode[]; - if (!isTaggedTemplate && !isDecorator) { + if (!isTaggedTemplate && !isDecorator && !isPipeline) { typeArguments = (node).typeArguments; // We already perform checking on the type arguments on the class declaration itself. @@ -12857,7 +12893,7 @@ namespace ts { reorderCandidates(signatures, candidates); if (!candidates.length) { reportError(Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target); - return resolveErrorCall(node); + return partialApplication ? [resolveErrorCall(node)] : resolveErrorCall(node); } const args = getEffectiveCallArguments(node); @@ -12914,7 +12950,7 @@ namespace ts { let candidateForArgumentError: Signature; let candidateForTypeArgumentError: Signature; let resultOfFailedInference: InferenceContext; - let result: Signature; + let result: Signature | Signature[]; // 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. @@ -12934,7 +12970,7 @@ namespace ts { if (candidates.length > 1) { result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma); } - if (!result) { + if (!result || partialApplication) { // Reinitialize these pointers for round two candidateForArgumentError = undefined; candidateForTypeArgumentError = undefined; @@ -12993,12 +13029,12 @@ namespace ts { if (candidate.typeParameters && typeArguments) { candidate = getSignatureInstantiation(candidate, map(typeArguments, getTypeFromTypeNode)); } - return candidate; + return partialApplication ? [candidate] : candidate; } } } - return resolveErrorCall(node); + return partialApplication ? [resolveErrorCall(node)] : resolveErrorCall(node); function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void { let errorInfo: DiagnosticMessageChain; @@ -13010,8 +13046,9 @@ namespace ts { diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo)); } - function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { - for (const originalCandidate of candidates) { + function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false): Signature | Signature[] { + let partialCandidates: Signature[]; + outer: for (const originalCandidate of candidates) { if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) { continue; } @@ -13045,6 +13082,10 @@ namespace ts { } const index = excludeArgument ? indexOf(excludeArgument, true) : -1; if (index < 0) { + if (partialApplication) { + partialCandidates = append(partialCandidates, originalCandidate); + continue outer; + } return candidate; } excludeArgument[index] = false; @@ -13073,12 +13114,14 @@ namespace ts { } } - return undefined; + return partialCandidates; } - } - function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature { + function resolveCallExpression(node: CallExpression, partialApplication: false, candidatesOutArray: Signature[]): Signature; + function resolveCallExpression(node: CallExpression, partialApplication: true, candidatesOutArray: Signature[]): Signature[]; + function resolveCallExpression(node: CallExpression, partialApplication: boolean, candidatesOutArray: Signature[]): Signature | Signature[]; + function resolveCallExpression(node: CallExpression, partialApplication: boolean, candidatesOutArray: Signature[]): Signature | Signature[] { if (node.expression.kind === SyntaxKind.SuperKeyword) { const superType = checkSuperExpression(node.expression); if (superType !== unknownType) { @@ -13087,7 +13130,7 @@ namespace ts { const baseTypeNode = getClassExtendsHeritageClauseElement(getContainingClass(node)); if (baseTypeNode) { const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments); - return resolveCall(node, baseConstructors, candidatesOutArray); + return resolveCall(node, baseConstructors, candidatesOutArray, partialApplication); } } return resolveUntypedCall(node); @@ -13134,7 +13177,7 @@ namespace ts { } return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray); + return resolveCall(node, callSignatures, candidatesOutArray, partialApplication); } /** @@ -13213,7 +13256,7 @@ namespace ts { if (!isConstructorAccessible(node, constructSignatures[0])) { return resolveErrorCall(node); } - return resolveCall(node, constructSignatures, candidatesOutArray); + return resolveCall(node, constructSignatures, candidatesOutArray, /*allowPartialApplication*/ false); } // If expressionType's apparent type is an object type with no construct signatures but @@ -13222,7 +13265,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); + const signature = resolveCall(node, callSignatures, candidatesOutArray, /*allowPartialApplication*/ false); if (getReturnTypeOfSignature(signature) !== voidType) { error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); } @@ -13299,7 +13342,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray); + return resolveCall(node, callSignatures, candidatesOutArray, /*partialApplication*/ false); } /** @@ -13349,19 +13392,44 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray, headMessage); + return resolveCall(node, callSignatures, candidatesOutArray, /*allowPartialApplication*/ false, headMessage); } - function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature { + function resolvePipelineExpression(node: PipelineExpression, candidatesOutArray: Signature[]): Signature { + const funcType = checkExpression(node.right); + const apparentType = getApparentType(funcType); + if (apparentType === unknownType) { + return resolveErrorCall(node); + } + + const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); + const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); + if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { + return resolveUntypedCall(node); + } + + if (!callSignatures.length) { + return resolveErrorCall(node); + } + + return resolveCall(node, callSignatures, candidatesOutArray, /*partialApplication*/ false); + } + + function resolveSignature(node: CallLikeExpression, partialApplication: false, candidatesOutArray?: Signature[]): Signature; + function resolveSignature(node: CallLikeExpression, partialApplication: true, candidatesOutArray?: Signature[]): Signature[]; + function resolveSignature(node: CallLikeExpression, partialApplication: boolean, candidatesOutArray?: Signature[]): Signature | Signature[]; + function resolveSignature(node: CallLikeExpression, partialApplication: boolean, candidatesOutArray?: Signature[]): Signature | Signature[] { switch (node.kind) { case SyntaxKind.CallExpression: - return resolveCallExpression(node, candidatesOutArray); + return resolveCallExpression(node, partialApplication, candidatesOutArray); case SyntaxKind.NewExpression: return resolveNewExpression(node, candidatesOutArray); case SyntaxKind.TaggedTemplateExpression: return resolveTaggedTemplateExpression(node, candidatesOutArray); case SyntaxKind.Decorator: return resolveDecorator(node, candidatesOutArray); + case SyntaxKind.BinaryExpression: + return resolvePipelineExpression(node, candidatesOutArray); } Debug.fail("Branch in 'resolveSignature' should be unreachable."); } @@ -13379,7 +13447,7 @@ namespace ts { return cached; } links.resolvedSignature = resolvingSignature; - const result = resolveSignature(node, candidatesOutArray); + const result = resolveSignature(node, /*allowPartialApplication*/ false, candidatesOutArray); // 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. @@ -13387,6 +13455,25 @@ namespace ts { return result; } + function getResolvedPartialSignatures(node: CallLikeExpression, candidatesOutArray?: Signature[]): 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, + // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work + // to correctly fill the candidatesOutArray. + const cached = links.resolvedPartialSignatures; + if (cached && cached !== resolvingPartialSignatures && !candidatesOutArray) { + return cached; + } + links.resolvedPartialSignatures = resolvingPartialSignatures; + const result = resolveSignature(node, /*partialApplication*/ true, candidatesOutArray); + // 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.resolvedPartialSignatures = flowLoopStart === flowLoopCount ? result : cached; + return result; + } + function getResolvedOrAnySignature(node: CallLikeExpression) { // If we're already in the process of resolving the given signature, don't resolve again as // that could cause infinite recursion. Instead, return anySignature. @@ -13410,7 +13497,9 @@ namespace ts { // Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments); - const signature = getResolvedSignature(node); + const partialApplication = node.kind === SyntaxKind.CallExpression && forEach(node.arguments, isPositionalOrPositionalSpreadElement); + const signatures = partialApplication && getResolvedPartialSignatures(node); + const signature = !partialApplication && getResolvedSignature(node); if (node.expression.kind === SyntaxKind.SuperKeyword) { return voidType; @@ -13448,7 +13537,123 @@ namespace ts { return resolveExternalModuleTypeByLiteral(node.arguments[0]); } - return getReturnTypeOfSignature(signature); + return partialApplication ? + getPartialApplicationOfSignatures(node.arguments, signatures) : + getReturnTypeOfSignature(signature); + } + + function getPartialApplicationOfSignatures(argumentList: Expression[], signatures: Signature[]) { + const partialSignatures: Signature[] = []; + for (const signature of signatures) { + partialSignatures.push(getPartialApplicationOfSignature(argumentList, signature)); + } + + const partialType = createObjectType(ObjectFlags.Anonymous); + (partialType).members = emptySymbols; + (partialType).properties = emptyArray; + (partialType).callSignatures = partialSignatures; + (partialType).constructSignatures = emptyArray; + return partialType; + } + + function getPartialApplicationOfSignature(argumentList: Expression[], signature: Signature) { + const uniqueParameterNames = createMap(); + for (const parameter of signature.parameters) { + uniqueParameterNames[parameter.name] = true; + } + + let positionalParameters: TransientSymbol[]; + let positionalRestParameter: TransientSymbol; + let position = 0; + for (let i = 0; i < argumentList.length; i++) { + const argument = argumentList[i]; + // const inRestParameter = signature.hasRestParameter && i >= signature.parameters.length; + const parameter = i < signature.parameters.length ? signature.parameters[i] : lastOrUndefined(signature.parameters); + if (isPositionalElement(argument)) { + let ordinalPosition: number; + if (argument.literal) { + ordinalPosition = +argument.literal.text; + } + else { + ordinalPosition = position; + position++; + } + if (!positionalParameters) { + positionalParameters = []; + } + const type = getTypeAtPosition(signature, i); + const previousParameter = positionalParameters[ordinalPosition]; + if (previousParameter) { + if (previousParameter.name === previousParameter.target.name) { + previousParameter.name = getUniqueName(uniqueParameterNames, `arg${ordinalPosition}`); + } + previousParameter.type = getIntersectionType([previousParameter.type, type]); + } + else if (parameter) { + positionalParameters[ordinalPosition] = createTransientSymbol(parameter, type); + } + else { + // TODO(rbuckton): implicit any error? + const name = getUniqueName(uniqueParameterNames, `arg${ordinalPosition}`); + const newParameter = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, name); + newParameter.type = type; + positionalParameters[ordinalPosition] = newParameter; + } + } + else if (isPositionalSpreadElement(argument)) { + const type = getRestTypeOfSignature(signature); + if (positionalRestParameter) { + if (positionalRestParameter.name === positionalRestParameter.target.name) { + positionalRestParameter.name = getUniqueName(uniqueParameterNames, `args`); + } + positionalRestParameter.type = getIntersectionType([positionalRestParameter.type, type]); + } + else if (parameter) { + positionalRestParameter = createTransientSymbol(parameter, type); + positionalRestParameter.transientSymbolIsRest = true; + } + else { + // TODO(rbuckton): implicit any error? + const name = getUniqueName(uniqueParameterNames, `args`); + positionalRestParameter = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, name); + positionalRestParameter.type = type; + positionalRestParameter.transientSymbolIsRest = true; + } + } + } + + positionalParameters = append(positionalParameters, positionalRestParameter) || []; + + for (let i = 0; i < positionalParameters.length; i++) { + if (!positionalParameters[i]) { + const name = getUniqueName(uniqueParameterNames, `arg${i}`); + positionalParameters[i] = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, name); + positionalParameters[i].type = anyType; + // TODO(rbuckton): implicit any error + } + } + + return createSignature( + /*declaration*/ undefined, + signature.typeParameters, + signature.thisParameter, + positionalParameters, + getReturnTypeOfSignature(signature), + signature.typePredicate, + position, + positionalRestParameter !== undefined, + signature.hasLiteralTypes); + } + + function getUniqueName(uniqueNames: Map, name: string) { + let uniqueName = name; + let index = 0; + while (uniqueNames[uniqueName]) { + uniqueName = `${name}_${index}`; + index++; + } + uniqueNames[uniqueName] = true; + return uniqueName; } function isCommonJsRequire(node: Node) { @@ -13959,6 +14164,112 @@ namespace ts { } } + function checkOperatorExpression(node: OperatorExpression) { + return operatorExpressionTypes[node.operator] || (operatorExpressionTypes[node.operator] = createOperatorExpressionType(node.operator)); + } + + function createBinaryOperatorType(typeParameters: TypeParameter[], leftType: Type, rightType: Type, returnType: Type) { + return getOrCreateTypeFromSignature(createBinaryOperatorSignature(typeParameters, leftType, rightType, returnType)); + } + + function createBinaryOperatorSignature(typeParameters: TypeParameter[], leftType: Type, rightType: Type, returnType: Type) { + const left = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, "a"); + left.type = leftType; + const right = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, "b"); + right.type = rightType; + return createSignature( + /*isConstruct*/ false, + typeParameters, + /*thisParameter*/ undefined, + [left, right], + returnType, + /*typePredicate*/ undefined, + 2, + /*hasRestParameter*/ false, + /*hasLiteralTypes*/ false + ); + } + + function createUnaryOperatorType(operandType: Type, returnType: Type) { + return getOrCreateTypeFromSignature(createUnaryOperatorSignature(operandType, returnType)); + } + + function createUnaryOperatorSignature(operandType: Type, returnType: Type) { + const operand = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, "a"); + operand.type = operandType; + return createSignature( + /*isConstruct*/ false, + /*typeParameters*/ undefined, + /*thisParameter*/ undefined, + [operand], + returnType, + /*typePredicate*/ undefined, + 1, + /*hasRestParameter*/ false, + /*hasLiteralTypes*/ false + ); + } + + function createOperatorExpressionType(operator: SyntaxKind) { + switch (operator) { + case SyntaxKind.PlusToken: + const signatures = [ + createBinaryOperatorSignature(undefined, stringType, stringType, stringType), + createBinaryOperatorSignature(undefined, stringType, numberType, stringType), + createBinaryOperatorSignature(undefined, numberType, stringType, stringType), + createBinaryOperatorSignature(undefined, numberType, numberType, numberType), + ] + const plusType = createObjectType(ObjectFlags.Anonymous); + (plusType).members = emptySymbols; + (plusType).properties = emptyArray; + (plusType).callSignatures = signatures; + (plusType).constructSignatures = emptyArray; + return plusType; + + case SyntaxKind.AsteriskToken: + case SyntaxKind.AsteriskAsteriskToken: + case SyntaxKind.SlashToken: + case SyntaxKind.PercentToken: + case SyntaxKind.MinusToken: + case SyntaxKind.LessThanLessThanToken: + case SyntaxKind.GreaterThanGreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + case SyntaxKind.BarToken: + case SyntaxKind.CaretToken: + case SyntaxKind.AmpersandToken: + return createBinaryOperatorType(undefined, numberType, numberType, numberType); + + case SyntaxKind.LessThanToken: + case SyntaxKind.GreaterThanToken: + case SyntaxKind.LessThanEqualsToken: + case SyntaxKind.GreaterThanEqualsToken: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + case SyntaxKind.InstanceOfKeyword: + case SyntaxKind.InKeyword: + // TODO: type parameters + return createBinaryOperatorType(undefined, anyType, anyType, booleanType); + + case SyntaxKind.AmpersandAmpersandToken: + case SyntaxKind.BarBarToken: + // TODO: type parameters + return createBinaryOperatorType(undefined, anyType, anyType, anyType); + + case SyntaxKind.TildeToken: + case SyntaxKind.TildePlusToken: + case SyntaxKind.TildeMinusToken: + return createUnaryOperatorType(anyType, numberType); + case SyntaxKind.ExclamationToken: + return createUnaryOperatorType(anyType, booleanType); + case SyntaxKind.VoidKeyword: + return createUnaryOperatorType(anyType, voidType); + case SyntaxKind.TypeOfKeyword: + return createUnaryOperatorType(anyType, stringType); + } + } + function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean { if (!isTypeAnyOrAllConstituentTypesHaveKind(type, TypeFlags.NumberLike)) { error(operand, diagnostic); @@ -14413,6 +14724,9 @@ namespace ts { } function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) { + if (node.operatorToken.kind === SyntaxKind.BarGreaterThanToken) { + return checkPipelineExpression(node); + } return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, contextualMapper, node); } @@ -14421,6 +14735,7 @@ namespace ts { if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) { return checkDestructuringAssignment(left, checkExpression(right, contextualMapper), contextualMapper); } + let leftType = checkExpression(left, contextualMapper); let rightType = checkExpression(right, contextualMapper); switch (operator) { @@ -14625,6 +14940,11 @@ namespace ts { } } + function checkPipelineExpression(node: PipelineExpression) { + const signature = getResolvedSignature(node); + return getReturnTypeOfSignature(signature); + } + function isYieldExpressionInClass(node: YieldExpression): boolean { let current: Node = node; let parent = node.parent; @@ -14890,6 +15210,8 @@ namespace ts { return checkTaggedTemplateExpression(node); case SyntaxKind.ParenthesizedExpression: return checkExpression((node).expression, contextualMapper); + case SyntaxKind.OperatorExpression: + return checkOperatorExpression(node); case SyntaxKind.ClassExpression: return checkClassExpression(node); case SyntaxKind.FunctionExpression: diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ed7f3ee16a0..241d51576e0 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -651,7 +651,11 @@ namespace ts { case SyntaxKind.YieldExpression: return emitYieldExpression(node); case SyntaxKind.SpreadElement: - return emitSpreadExpression(node); + return emitSpreadElement(node); + case SyntaxKind.PositionalElement: + return emitPositionalElement(node); + case SyntaxKind.OperatorExpression: + return emitOperatorExpression(node); case SyntaxKind.ClassExpression: return emitClassExpression(node); case SyntaxKind.OmittedExpression: @@ -1222,11 +1226,22 @@ namespace ts { emitExpressionWithPrefix(" ", node.expression); } - function emitSpreadExpression(node: SpreadElement) { + function emitSpreadElement(node: SpreadElement) { write("..."); emitExpression(node.expression); } + function emitPositionalElement(node: PositionalElement) { + write("?"); + emitExpression(node.literal); + } + + function emitOperatorExpression(node: OperatorExpression) { + write("("); + writeTokenText(node.operator); + write(")"); + } + function emitClassExpression(node: ClassExpression) { emitClassDeclarationOrExpression(node); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index d797fff92f4..76c4bcd3d6d 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -225,13 +225,12 @@ namespace ts { } // Signature elements - - export function createParameter(decorators: Decorator[], modifiers: Modifier[], dotDotDotToken: DotDotDotToken, name: string | Identifier | BindingPattern, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression, location?: TextRange, flags?: NodeFlags) { - const node = createNode(SyntaxKind.Parameter, location, flags); + export function createParameter(decorators?: Decorator[], modifiers?: Modifier[], dotDotDotToken?: DotDotDotToken, name?: string | Identifier | BindingPattern, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.Parameter, location); node.decorators = decorators ? createNodeArray(decorators) : undefined; node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.dotDotDotToken = dotDotDotToken; - node.name = typeof name === "string" ? createIdentifier(name) : name; + node.name = typeof name === "string" ? createIdentifier(name) : name || createTempVariable(/*recordTempVariable*/ undefined); node.questionToken = questionToken; node.type = type; node.initializer = initializer ? parenthesizeExpressionForList(initializer) : undefined; @@ -240,7 +239,7 @@ namespace ts { export function updateParameter(node: ParameterDeclaration, decorators: Decorator[], modifiers: Modifier[], dotDotDotToken: DotDotDotToken, name: BindingName, type: TypeNode, initializer: Expression) { if (node.decorators !== decorators || node.modifiers !== modifiers || node.dotDotDotToken !== dotDotDotToken || node.name !== name || node.type !== type || node.initializer !== initializer) { - return updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, node.questionToken, type, initializer, /*location*/ node, /*flags*/ node.flags), node); + return updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, node.questionToken, type, initializer, /*location*/ node), node); } return node; @@ -703,7 +702,7 @@ namespace ts { return node; } - export function createSpread(expression: Expression, location?: TextRange) { + export function createSpreadElement(expression: Expression, location?: TextRange) { const node = createNode(SyntaxKind.SpreadElement, location); node.expression = parenthesizeExpressionForList(expression); return node; @@ -711,7 +710,7 @@ namespace ts { export function updateSpread(node: SpreadElement, expression: Expression) { if (node.expression !== expression) { - return updateNode(createSpread(expression, node), node); + return updateNode(createSpreadElement(expression, node), node); } return node; } @@ -1787,7 +1786,7 @@ namespace ts { } } - export function createCallBinding(expression: Expression, recordTempVariable: (temp: Identifier) => void, languageVersion?: ScriptTarget, cacheIdentifiers?: boolean): CallBinding { + export function createCallBinding(expression: Expression, recordTempVariable: (temp: Identifier) => void, languageVersion?: ScriptTarget, cacheIdentifiers?: boolean, useThisAsDefaultThisArg?: boolean): CallBinding { const callee = skipOuterExpressions(expression, OuterExpressionKinds.All); let thisArg: Expression; let target: LeftHandSideExpression; @@ -1846,7 +1845,7 @@ namespace ts { default: { // for `a()` target is `a` and thisArg is `void 0` - thisArg = createVoidZero(); + thisArg = useThisAsDefaultThisArg ? createThis() : createVoidZero(); target = parenthesizeForAccess(expression); break; } @@ -3032,7 +3031,7 @@ namespace ts { return bindingElement.right; } - if (isSpreadExpression(bindingElement)) { + if (isSpreadElement(bindingElement)) { // Recovery consistent with existing emit. return getInitializerOfBindingOrAssignmentElement(bindingElement.expression); } @@ -3100,7 +3099,7 @@ namespace ts { return getTargetOfBindingOrAssignmentElement(bindingElement.left); } - if (isSpreadExpression(bindingElement)) { + if (isSpreadElement(bindingElement)) { // `a` in `[...a] = ...` return getTargetOfBindingOrAssignmentElement(bindingElement.expression); } @@ -3202,7 +3201,7 @@ namespace ts { if (isBindingElement(element)) { if (element.dotDotDotToken) { Debug.assertNode(element.name, isIdentifier); - return setOriginalNode(createSpread(element.name, element), element); + return setOriginalNode(createSpreadElement(element.name, element), element); } const expression = convertToAssignmentElementTarget(element.name); return element.initializer ? setOriginalNode(createAssignment(expression, element.initializer, element), element) : expression; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b08f75b0f5b..7f9388b8481 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1319,7 +1319,7 @@ namespace ts { return isIdentifier(); case ParsingContext.ArgumentExpressions: case ParsingContext.ArrayLiteralMembers: - return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isStartOfExpression(); + return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || token() === SyntaxKind.QuestionToken || isStartOfExpression(); case ParsingContext.Parameters: return isStartOfParameter(); case ParsingContext.TypeArguments: @@ -3361,21 +3361,23 @@ namespace ts { function getBinaryOperatorPrecedence(): number { switch (token()) { - case SyntaxKind.BarBarToken: + case SyntaxKind.BarGreaterThanToken: return 1; - case SyntaxKind.AmpersandAmpersandToken: + case SyntaxKind.BarBarToken: return 2; - case SyntaxKind.BarToken: + case SyntaxKind.AmpersandAmpersandToken: return 3; - case SyntaxKind.CaretToken: + case SyntaxKind.BarToken: return 4; - case SyntaxKind.AmpersandToken: + case SyntaxKind.CaretToken: return 5; + case SyntaxKind.AmpersandToken: + return 6; case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: - return 6; + return 7; case SyntaxKind.LessThanToken: case SyntaxKind.GreaterThanToken: case SyntaxKind.LessThanEqualsToken: @@ -3383,20 +3385,20 @@ namespace ts { case SyntaxKind.InstanceOfKeyword: case SyntaxKind.InKeyword: case SyntaxKind.AsKeyword: - return 7; + return 8; case SyntaxKind.LessThanLessThanToken: case SyntaxKind.GreaterThanGreaterThanToken: case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - return 8; + return 9; case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: - return 9; + return 10; case SyntaxKind.AsteriskToken: case SyntaxKind.SlashToken: case SyntaxKind.PercentToken: - return 10; - case SyntaxKind.AsteriskAsteriskToken: return 11; + case SyntaxKind.AsteriskAsteriskToken: + return 12; } // -1 is lower than all other precedences. Returning it will cause binary expression @@ -4171,7 +4173,12 @@ namespace ts { return parseIdentifier(Diagnostics.Expression_expected); } - function parseParenthesizedExpression(): ParenthesizedExpression { + function parseParenthesizedExpression(): PrimaryExpression { + const operatorExpression = tryParse(parsePossibleOperatorExpression); + if (operatorExpression) { + return operatorExpression; + } + const node = createNode(SyntaxKind.ParenthesizedExpression); parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); @@ -4179,21 +4186,102 @@ namespace ts { return finishNode(node); } - function parseSpreadElement(): Expression { + function parsePossibleOperatorExpression() { + const fullStart = scanner.getStartPos(); + parseExpected(SyntaxKind.OpenParenToken); + + let operator = token(); + switch (operator) { + case SyntaxKind.AsteriskAsteriskToken: + case SyntaxKind.AsteriskToken: + case SyntaxKind.SlashToken: + case SyntaxKind.PercentToken: + case SyntaxKind.PlusToken: + case SyntaxKind.MinusToken: + case SyntaxKind.LessThanLessThanToken: + case SyntaxKind.GreaterThanGreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + case SyntaxKind.LessThanToken: + case SyntaxKind.LessThanEqualsToken: + case SyntaxKind.GreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanEqualsToken: + case SyntaxKind.InstanceOfKeyword: + case SyntaxKind.InKeyword: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + case SyntaxKind.AmpersandToken: + case SyntaxKind.BarToken: + case SyntaxKind.CaretToken: + case SyntaxKind.AmpersandAmpersandToken: + case SyntaxKind.BarBarToken: + case SyntaxKind.ExclamationToken: + case SyntaxKind.VoidKeyword: + case SyntaxKind.TypeOfKeyword: + nextToken(); + if (token() === SyntaxKind.CloseParenToken) { + nextToken(); + const node = createNode(SyntaxKind.OperatorExpression, fullStart); + node.operator = operator; + return finishNode(node); + } + return undefined; + case SyntaxKind.TildeToken: + nextToken(); + if (token() === SyntaxKind.PlusToken) { + nextToken(); + operator = SyntaxKind.TildePlusToken; + } + else if (token() === SyntaxKind.MinusToken) { + nextToken(); + operator = SyntaxKind.TildeMinusToken; + } + if (token() === SyntaxKind.CloseParenToken) { + nextToken(); + const node = createNode(SyntaxKind.OperatorExpression, fullStart); + node.operator = operator; + return finishNode(node); + } + return undefined; + default: + return undefined; + } + } + + function parseSpreadElement(isArgument: boolean): Expression { const node = createNode(SyntaxKind.SpreadElement); parseExpected(SyntaxKind.DotDotDotToken); - node.expression = parseAssignmentExpressionOrHigher(); + node.expression = !isArgument || isStartOfExpression() ? + parseAssignmentExpressionOrHigher() : + createNode(SyntaxKind.OmittedExpression); return finishNode(node); } - function parseArgumentOrArrayLiteralElement(): Expression { - return token() === SyntaxKind.DotDotDotToken ? parseSpreadElement() : + function parsePositionalElement(): Expression { + const node = createNode(SyntaxKind.PositionalElement); + parseExpected(SyntaxKind.QuestionToken); + node.literal = token() === SyntaxKind.NumericLiteral ? parseLiteralNode() : undefined; + return finishNode(node); + } + + function parseArgument() { + return parseArgumentOrArrayLiteralElement(/*isArgument*/ true); + } + + function parseArrayLiteralElement() { + return parseArgumentOrArrayLiteralElement(/*isArgument*/ false); + } + + function parseArgumentOrArrayLiteralElement(isArgument: boolean): Expression { + return token() === SyntaxKind.DotDotDotToken ? parseSpreadElement(isArgument) : token() === SyntaxKind.CommaToken ? createNode(SyntaxKind.OmittedExpression) : + isArgument && token() === SyntaxKind.QuestionToken ? parsePositionalElement() : parseAssignmentExpressionOrHigher(); } function parseArgumentExpression(): Expression { - return doOutsideOfContext(disallowInAndDecoratorContext, parseArgumentOrArrayLiteralElement); + return doOutsideOfContext(disallowInAndDecoratorContext, parseArgument); } function parseArrayLiteralExpression(): ArrayLiteralExpression { @@ -4202,7 +4290,7 @@ namespace ts { if (scanner.hasPrecedingLineBreak()) { node.multiLine = true; } - node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArgumentOrArrayLiteralElement); + node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArrayLiteralElement); parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 82302e98e37..a20ad00e7ce 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -135,6 +135,7 @@ namespace ts { "]": SyntaxKind.CloseBracketToken, ".": SyntaxKind.DotToken, "...": SyntaxKind.DotDotDotToken, + "|>": SyntaxKind.BarGreaterThanToken, ";": SyntaxKind.SemicolonToken, ",": SyntaxKind.CommaToken, "<": SyntaxKind.LessThanToken, @@ -163,6 +164,8 @@ namespace ts { "^": SyntaxKind.CaretToken, "!": SyntaxKind.ExclamationToken, "~": SyntaxKind.TildeToken, + "~+": SyntaxKind.TildePlusToken, + "~-": SyntaxKind.TildeMinusToken, "&&": SyntaxKind.AmpersandAmpersandToken, "||": SyntaxKind.BarBarToken, "?": SyntaxKind.QuestionToken, @@ -1551,6 +1554,9 @@ namespace ts { if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { return pos += 2, token = SyntaxKind.BarEqualsToken; } + if (text.charCodeAt(pos + 1) === CharacterCodes.greaterThan) { + return pos += 2, token = SyntaxKind.BarGreaterThanToken; + } pos++; return token = SyntaxKind.BarToken; case CharacterCodes.closeBrace: diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 838e58ae99b..4dac7d22991 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -2911,7 +2911,7 @@ namespace ts { if (segments.length === 1) { const firstElement = elements[0]; - return needsUniqueCopy && isSpreadExpression(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression + return needsUniqueCopy && isSpreadElement(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression ? createArraySlice(segments[0]) : segments[0]; } @@ -2921,7 +2921,7 @@ namespace ts { } function partitionSpread(node: Expression) { - return isSpreadExpression(node) + return isSpreadElement(node) ? visitSpanOfSpreads : visitSpanOfNonSpreads; } diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 0a529a8e871..23404fa93df 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -5,6 +5,7 @@ namespace ts { export function transformESNext(context: TransformationContext) { const { + startLexicalEnvironment, resumeLexicalEnvironment, endLexicalEnvironment } = context; @@ -66,6 +67,10 @@ namespace ts { return visitExpressionStatement(node as ExpressionStatement); case SyntaxKind.ParenthesizedExpression: return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue); + case SyntaxKind.CallExpression: + return visitCallExpression(node as CallExpression); + case SyntaxKind.OperatorExpression: + return visitOperatorExpression(node as OperatorExpression); default: return visitEachChild(node, visitor, context); } @@ -142,16 +147,64 @@ namespace ts { !noDestructuringValue ); } - else if (node.operatorToken.kind === SyntaxKind.CommaToken) { - return updateBinary( - node, - visitNode(node.left, visitorNoDestructuringValue, isExpression), - visitNode(node.right, noDestructuringValue ? visitorNoDestructuringValue : visitor, isExpression) - ); + switch (node.operatorToken.kind) { + case SyntaxKind.CommaToken: + return updateBinary( + node, + visitNode(node.left, visitorNoDestructuringValue, isExpression), + visitNode(node.right, noDestructuringValue ? visitorNoDestructuringValue : visitor, isExpression) + ); + case SyntaxKind.BarGreaterThanToken: + return transformPipelineExpression(node); } return visitEachChild(node, visitor, context); } + function transformPipelineExpression(node: BinaryExpression) { + const argumentList: Expression[] = []; + let expression = node.left; + if (expression.kind === SyntaxKind.ParenthesizedExpression) { + // comma expressions are right-deep + expression = (expression).expression; + while (expression.kind === SyntaxKind.BinaryExpression && + (expression).operatorToken.kind === SyntaxKind.CommaToken) { + argumentList.push(visitNode((expression).left, visitor, isExpression)); + expression = (expression).right; + } + } + argumentList.push(visitNode(expression, visitor, isExpression)); + const func = visitNode(node.right, visitor, isExpression); + if (func.kind === SyntaxKind.ArrowFunction || + func.kind === SyntaxKind.FunctionExpression) { + return createCall(func, /*typeArguments*/ undefined, argumentList); + } + else { + const parameterList: ParameterDeclaration[] = []; + const innerArgumentList: Expression[] = []; + for (let i = 0; i < argumentList.length; i++) { + const parameter = createParameter(); + parameterList.push(parameter); + innerArgumentList.push(parameter.name); + } + return createCall( + createArrowFunction( + /*modifiers*/ undefined, + /*typeParameters*/ undefined, + parameterList, + /*type*/ undefined, + createToken(SyntaxKind.EqualsGreaterThanToken), + createCall( + func, + /*typeArguments*/ undefined, + innerArgumentList + ) + ), + /*typeArguments*/ undefined, + argumentList + ); + } + } + /** * Visits a VariableDeclaration node with a binding pattern. * @@ -384,6 +437,106 @@ namespace ts { } return body; } + + function visitCallExpression(node: CallExpression): Expression { + const expression = visitNode(node.expression, visitor, isExpression); + let position = 0; + let positionalParameters: ParameterDeclaration[]; + let positionalRestParameter: ParameterDeclaration; + let argumentList: Expression[]; + for (let i = 0; i < node.arguments.length; i++) { + const argument = node.arguments[i]; + let updated: Expression; + if (isPositionalElement(argument)) { + if (!positionalParameters) { + positionalParameters = []; + } + if (argument.literal) { + position = +argument.literal.text; + } + const parameter = positionalParameters[position] || (positionalParameters[position] = createParameter()); + updated = parameter.name; + position++; + } + else if (isPositionalSpreadElement(argument)) { + const parameter = positionalRestParameter || (positionalRestParameter = createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, createToken(SyntaxKind.DotDotDotToken))); + updated = createSpreadElement(parameter.name); + } + else { + updated = visitNode(argument, visitor, isExpression); + } + if (argumentList || updated !== argument) { + if (!argumentList) { + argumentList = node.arguments.slice(0, i); + } + argumentList.push(updated); + } + } + if (positionalParameters || positionalRestParameter) { + startLexicalEnvironment(); + if (positionalRestParameter) { + positionalParameters = append(positionalParameters, positionalRestParameter); + } + for (let i = 0; i < positionalParameters.length; i++) { + if (!positionalParameters[i]) { + positionalParameters[i] = createParameter(); + } + } + return createArrowFunction( + /*modifiers*/ undefined, + /*typeParameters*/ undefined, + positionalParameters, + /*type*/ undefined, + /*equalsGreaterThanToken*/ createToken(SyntaxKind.EqualsGreaterThanToken), + updateCall(node, expression, /*typeArguments*/ undefined, argumentList), + /*location*/ node + ); + } + return updateCall(node, expression, /*typeArguments*/ undefined, argumentList); + } + + function visitOperatorExpression(node: OperatorExpression) { + let parameters: ParameterDeclaration[]; + let expression: Expression; + if (isBinaryOperator(node.operator)) { + parameters = [createParameter(), createParameter()]; + expression = createBinary( + parameters[0].name, + node.operator, + parameters[1].name + ); + } + else { + parameters = [createParameter()]; + switch (node.operator) { + case SyntaxKind.TildePlusToken: + expression = createPrefix(SyntaxKind.PlusToken, parameters[0].name, node); + break; + case SyntaxKind.TildeMinusToken: + expression = createPrefix(SyntaxKind.MinusToken, parameters[0].name, node); + break; + case SyntaxKind.TildeToken: + case SyntaxKind.ExclamationToken: + expression = createPrefix(node.operator, parameters[0].name, node); + break; + case SyntaxKind.VoidKeyword: + expression = createVoid(parameters[0].name, node); + break; + case SyntaxKind.TypeOfKeyword: + expression = createTypeOf(parameters[0].name, node); + break; + } + } + return createArrowFunction( + /*modifiers*/ undefined, + /*typeParameters*/ undefined, + parameters, + /*type*/ undefined, + createToken(SyntaxKind.EqualsGreaterThanToken), + expression, + node + ); + } } const assignHelper: EmitHelper = { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 91e29e09886..8c420d5ba93 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1497,7 +1497,7 @@ namespace ts { if (isAssignmentExpression(node)) { return hasExportedReferenceInDestructuringTarget(node.left); } - else if (isSpreadExpression(node)) { + else if (isSpreadElement(node)) { return hasExportedReferenceInDestructuringTarget(node.expression); } else if (isObjectLiteralExpression(node)) { diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 62b2022b7df..96f74d8ad0a 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -892,7 +892,7 @@ namespace ts { createCall( createSuper(), /*typeArguments*/ undefined, - [createSpread(createIdentifier("arguments"))] + [createSpreadElement(createIdentifier("arguments"))] ) ) ); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b5e3eefbb2d..92bf6be28b2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -62,7 +62,9 @@ namespace ts { DotToken, DotDotDotToken, SemicolonToken, + BarGreaterThanToken, CommaToken, + EqualsGreaterThanToken, LessThanToken, LessThanSlashToken, GreaterThanToken, @@ -72,7 +74,6 @@ namespace ts { ExclamationEqualsToken, EqualsEqualsEqualsToken, ExclamationEqualsEqualsToken, - EqualsGreaterThanToken, PlusToken, MinusToken, AsteriskToken, @@ -89,6 +90,8 @@ namespace ts { CaretToken, ExclamationToken, TildeToken, + TildePlusToken, + TildeMinusToken, AmpersandAmpersandToken, BarBarToken, QuestionToken, @@ -248,11 +251,13 @@ namespace ts { TemplateExpression, YieldExpression, SpreadElement, + PositionalElement, ClassExpression, OmittedExpression, ExpressionWithTypeArguments, AsExpression, NonNullExpression, + OperatorExpression, // Misc TemplateSpan, @@ -1172,6 +1177,7 @@ namespace ts { // see: https://tc39.github.io/ecma262/#prod-Expression export type BinaryOperator = AssignmentOperatorOrHigher + | SyntaxKind.BarGreaterThanToken | SyntaxKind.CommaToken ; @@ -1185,6 +1191,12 @@ namespace ts { right: Expression; } + export type BarGreaterThanToken = Token; + + export interface PipelineExpression extends BinaryExpression { + operatorToken: BarGreaterThanToken; + } + export type AssignmentOperatorToken = Token; export interface AssignmentExpression extends BinaryExpression { @@ -1332,6 +1344,11 @@ namespace ts { expression: Expression; } + export interface OperatorExpression extends PrimaryExpression { + kind: SyntaxKind.OperatorExpression; + operator: SyntaxKind; + } + export interface ArrayLiteralExpression extends PrimaryExpression { kind: SyntaxKind.ArrayLiteralExpression; elements: NodeArray; @@ -1344,6 +1361,10 @@ namespace ts { expression: Expression; } + export interface PositionalSpreadElement extends SpreadElement { + expression: OmittedExpression; + } + /** * This interface is a base interface for ObjectLiteralExpression and JSXAttributes to extend from. JSXAttributes is similar to * ObjectLiteralExpression in that it contains array of properties; however, JSXAttributes' properties can only be @@ -1427,7 +1448,12 @@ namespace ts { template: TemplateLiteral; } - export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator; + export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | PipelineExpression; + + export interface PositionalElement extends Expression { + kind: SyntaxKind.PositionalElement; + literal?: NumericLiteral; + } export interface AsExpression extends Expression { kind: SyntaxKind.AsExpression; @@ -2688,7 +2714,9 @@ namespace ts { } /* @internal */ - export interface TransientSymbol extends Symbol, SymbolLinks { } + export interface TransientSymbol extends Symbol, SymbolLinks { + transientSymbolIsRest?: boolean; + } export type SymbolTable = Map; @@ -2735,6 +2763,7 @@ namespace ts { flags?: NodeCheckFlags; // Set of flags specific to Node resolvedType?: Type; // Cached type of type node resolvedSignature?: Signature; // Cached signature of signature node or call expression + resolvedPartialSignatures?: Signature[]; resolvedSymbol?: Symbol; // Cached name resolution result resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate @@ -3021,6 +3050,8 @@ namespace ts { typePredicate?: TypePredicate; /* @internal */ instantiations?: Map; // Generic signature instantiation cache + /* @internal */ + isConstruct?: boolean; } export const enum IndexKind { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 546416884f1..5979ef34c0e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3876,6 +3876,38 @@ namespace ts { return node.kind === SyntaxKind.ElementAccessExpression; } + export function isBinaryOperator(operator: SyntaxKind): operator is BinaryOperator { + switch (operator) { + case SyntaxKind.AsteriskAsteriskToken: + case SyntaxKind.AsteriskToken: + case SyntaxKind.SlashToken: + case SyntaxKind.PercentToken: + case SyntaxKind.PlusToken: + case SyntaxKind.MinusToken: + case SyntaxKind.LessThanLessThanToken: + case SyntaxKind.GreaterThanGreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + case SyntaxKind.LessThanToken: + case SyntaxKind.LessThanEqualsToken: + case SyntaxKind.GreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanEqualsToken: + case SyntaxKind.InstanceOfKeyword: + case SyntaxKind.InKeyword: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + case SyntaxKind.AmpersandToken: + case SyntaxKind.BarToken: + case SyntaxKind.CaretToken: + case SyntaxKind.AmpersandAmpersandToken: + case SyntaxKind.BarBarToken: + case SyntaxKind.BarGreaterThanToken: + return true; + } + return false; + } + export function isBinaryExpression(node: Node): node is BinaryExpression { return node.kind === SyntaxKind.BinaryExpression; } @@ -3894,10 +3926,23 @@ namespace ts { || kind === SyntaxKind.NoSubstitutionTemplateLiteral; } - export function isSpreadExpression(node: Node): node is SpreadElement { + export function isSpreadElement(node: Node): node is SpreadElement { return node.kind === SyntaxKind.SpreadElement; } + export function isPositionalSpreadElement(node: Node): node is PositionalSpreadElement { + return node.kind === SyntaxKind.SpreadElement + && (node).expression.kind === SyntaxKind.OmittedExpression; + } + + export function isPositionalElement(node: Node): node is PositionalElement { + return node.kind === SyntaxKind.PositionalElement; + } + + export function isPositionalOrPositionalSpreadElement(node: Node): node is PositionalElement | PositionalSpreadElement { + return isPositionalElement(node) || isPositionalSpreadElement(node); + } + export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { return node.kind === SyntaxKind.ExpressionWithTypeArguments; } @@ -3912,6 +3957,7 @@ namespace ts { || kind === SyntaxKind.TaggedTemplateExpression || kind === SyntaxKind.ArrayLiteralExpression || kind === SyntaxKind.ParenthesizedExpression + || kind === SyntaxKind.OperatorExpression || kind === SyntaxKind.ObjectLiteralExpression || kind === SyntaxKind.ClassExpression || kind === SyntaxKind.FunctionExpression @@ -3955,6 +4001,7 @@ namespace ts { || kind === SyntaxKind.ArrowFunction || kind === SyntaxKind.BinaryExpression || kind === SyntaxKind.SpreadElement + || kind === SyntaxKind.PositionalElement || kind === SyntaxKind.AsExpression || kind === SyntaxKind.OmittedExpression || kind === SyntaxKind.RawExpression