Initial support, tests to follow

This commit is contained in:
Ron Buckton 2017-10-05 18:43:05 -07:00
parent 924460406e
commit dc703e540c
10 changed files with 207 additions and 61 deletions

View File

@ -2737,7 +2737,11 @@ namespace ts {
const operatorTokenKind = node.operatorToken.kind;
const leftKind = node.left.kind;
if (operatorTokenKind === SyntaxKind.EqualsToken && leftKind === SyntaxKind.ObjectLiteralExpression) {
if (operatorTokenKind === SyntaxKind.BarGreaterThanToken) {
// pipeline is ESNext
transformFlags |= TransformFlags.AssertESNext;
}
else if (operatorTokenKind === SyntaxKind.EqualsToken && leftKind === SyntaxKind.ObjectLiteralExpression) {
// Destructuring object assignments with are ES2015 syntax
// and possibly ESNext if they contain rest
transformFlags |= TransformFlags.AssertESNext | TransformFlags.AssertES2015 | TransformFlags.AssertDestructuringAssignment;

View File

@ -6319,7 +6319,7 @@ namespace ts {
if (iife) {
return !node.type &&
!node.dotDotDotToken &&
indexOf((node.parent as SignatureDeclaration).parameters, node) >= iife.arguments.length;
indexOf((node.parent as SignatureDeclaration).parameters, node) >= (isPipelineExpression(iife) ? 1 : iife.arguments.length);
}
return false;
@ -6430,7 +6430,7 @@ namespace ts {
// Record a new minimum argument count if this is not an optional parameter
const isOptionalParameter = param.initializer || param.questionToken || param.dotDotDotToken ||
iife && parameters.length > iife.arguments.length && !param.type ||
iife && parameters.length > (isPipelineExpression(iife) ? 1 : iife.arguments.length) && !param.type ||
isJSDocOptionalParameter(param) ||
isUntypedSignatureInJSFile;
if (!isOptionalParameter) {
@ -12371,7 +12371,8 @@ namespace ts {
const parent = node.parent;
return parent.kind === SyntaxKind.PropertyAccessExpression ||
parent.kind === SyntaxKind.CallExpression && (<CallExpression>parent).expression === node ||
parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>parent).expression === node;
parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>parent).expression === node ||
isPipelineExpression(parent) && parent.right === node;
}
function typeHasNullableConstraint(type: Type) {
@ -13089,20 +13090,21 @@ namespace ts {
const func = parameter.parent;
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
const iife = getImmediatelyInvokedFunctionExpression(func);
if (iife && iife.arguments) {
if (iife && (isPipelineExpression(iife) || iife.arguments)) {
const args = getEffectiveCallArguments(iife);
const indexOfParameter = indexOf(func.parameters, parameter);
if (parameter.dotDotDotToken) {
const restTypes: Type[] = [];
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
restTypes.push(getWidenedLiteralType(checkExpression(iife.arguments[i])));
for (let i = indexOfParameter; i < args.length; i++) {
restTypes.push(getWidenedLiteralType(checkExpression(args[i])));
}
return restTypes.length ? createArrayType(getUnionType(restTypes)) : undefined;
}
const links = getNodeLinks(iife);
const cached = links.resolvedSignature;
links.resolvedSignature = anySignature;
const type = indexOfParameter < iife.arguments.length ?
getWidenedLiteralType(checkExpression(iife.arguments[indexOfParameter])) :
const type = indexOfParameter < args.length ?
getWidenedLiteralType(checkExpression(args[indexOfParameter])) :
parameter.initializer ? undefined : undefinedWideningType;
links.resolvedSignature = cached;
return type;
@ -13291,6 +13293,11 @@ namespace ts {
return getContextualType(binaryExpression);
}
}
else if (operator === SyntaxKind.BarGreaterThanToken) {
if (node === binaryExpression.left) {
return getContextualTypeForArgument(<PipelineExpression>binaryExpression, node);
}
}
return undefined;
}
@ -15296,7 +15303,10 @@ namespace ts {
}
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
checkExpression((<TaggedTemplateExpression>node).template);
checkExpression(node.template);
}
else if (node.kind === SyntaxKind.BinaryExpression) {
checkExpression(node.left);
}
else if (node.kind !== SyntaxKind.Decorator) {
forEach((<CallExpression>node).arguments, argument => {
@ -15415,6 +15425,10 @@ namespace ts {
typeArguments = undefined;
argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
}
else if (node.kind === SyntaxKind.BinaryExpression) {
typeArguments = undefined;
argCount = getEffectiveArgumentCount(node, args, signature);
}
else {
const callExpression = <CallExpression | NewExpression>node;
if (!callExpression.arguments) {
@ -15690,8 +15704,12 @@ namespace ts {
* Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise.
*/
function getThisArgumentOfCall(node: CallLikeExpression): LeftHandSideExpression {
if (node.kind === SyntaxKind.CallExpression) {
const callee = (<CallExpression>node).expression;
let callee = node.kind === SyntaxKind.CallExpression ? node.expression :
node.kind === SyntaxKind.Decorator ? node.expression :
node.kind === SyntaxKind.TaggedTemplateExpression ? node.tag :
node.kind === SyntaxKind.BinaryExpression ? node.right :
undefined;
if (callee) {
if (callee.kind === SyntaxKind.PropertyAccessExpression) {
return (callee as PropertyAccessExpression).expression;
}
@ -15712,15 +15730,18 @@ namespace ts {
*/
function getEffectiveCallArguments(node: CallLikeExpression): ReadonlyArray<Expression> {
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
const template = (<TaggedTemplateExpression>node).template;
const template = node.template;
const args: Expression[] = [undefined];
if (template.kind === SyntaxKind.TemplateExpression) {
forEach((<TemplateExpression>template).templateSpans, span => {
forEach(template.templateSpans, span => {
args.push(span.expression);
});
}
return args;
}
else if (node.kind === SyntaxKind.BinaryExpression) {
return [node.left];
}
else if (node.kind === SyntaxKind.Decorator) {
// For a decorator, we return undefined as we will determine
// the number and types of arguments for a decorator using
@ -16008,21 +16029,22 @@ namespace ts {
}
}
function getEffectiveTypeArguments(node: CallLikeExpression): NodeArray<TypeNode> {
if (node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression) {
const typeArguments = node.typeArguments;
// We already perform checking on the type arguments on the class declaration itself.
if (node.expression.kind !== SyntaxKind.SuperKeyword) {
forEach(typeArguments, checkSourceElement);
}
return typeArguments;
}
}
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], fallbackError?: DiagnosticMessage): Signature {
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
const isDecorator = node.kind === SyntaxKind.Decorator;
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
let typeArguments: ReadonlyArray<TypeNode>;
if (!isTaggedTemplate && !isDecorator && !isJsxOpeningOrSelfClosingElement) {
typeArguments = (<CallExpression>node).typeArguments;
// We already perform checking on the type arguments on the class declaration itself.
if ((<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
forEach(typeArguments, checkSourceElement);
}
}
const typeArguments = getEffectiveTypeArguments(node);
const candidates = candidatesOutArray || [];
// reorderCandidates fills up the candidates array directly
@ -16578,6 +16600,27 @@ namespace ts {
signature.parameters.length < getEffectiveArgumentCount(decorator, /*args*/ undefined, signature));
}
function resolvePipelineExpression(node: PipelineExpression, candidatesOutArray: Signature[]): Signature {
const type = checkNonNullExpression(node.right);
const apparentType = getApparentType(type);
if (apparentType === unknownType) {
return resolveErrorCall(node);
}
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
if (isUntypedFunctionCall(type, apparentType, callSignatures.length, constructSignatures.length)) {
return resolveUntypedCall(node);
}
if (!callSignatures.length) {
error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType));
return resolveErrorCall(node);
}
return resolveCall(node, callSignatures, candidatesOutArray);
}
/**
* This function is similar to getResolvedSignature but is exclusively for trying to resolve JSX stateless-function component.
* The main reason we have to use this function instead of getResolvedSignature because, the caller of this function will already check the type of openingLikeElement's tagName
@ -16625,17 +16668,19 @@ namespace ts {
function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
switch (node.kind) {
case SyntaxKind.CallExpression:
return resolveCallExpression(<CallExpression>node, candidatesOutArray);
return resolveCallExpression(node, candidatesOutArray);
case SyntaxKind.NewExpression:
return resolveNewExpression(<NewExpression>node, candidatesOutArray);
return resolveNewExpression(node, candidatesOutArray);
case SyntaxKind.TaggedTemplateExpression:
return resolveTaggedTemplateExpression(<TaggedTemplateExpression>node, candidatesOutArray);
return resolveTaggedTemplateExpression(node, candidatesOutArray);
case SyntaxKind.Decorator:
return resolveDecorator(<Decorator>node, candidatesOutArray);
return resolveDecorator(node, candidatesOutArray);
case SyntaxKind.BinaryExpression:
return resolvePipelineExpression(node, candidatesOutArray);
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxSelfClosingElement:
// This code-path is called by language service
return resolveStatelessJsxOpeningLikeElement(<JsxOpeningLikeElement>node, checkExpression((<JsxOpeningLikeElement>node).tagName), candidatesOutArray) || unknownSignature;
return resolveStatelessJsxOpeningLikeElement(node, checkExpression(node.tagName), candidatesOutArray) || unknownSignature;
}
Debug.assertNever(node, "Branch in 'resolveSignature' should be unreachable.");
}
@ -17828,6 +17873,9 @@ namespace ts {
}
function checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) {
if (node.operatorToken.kind === SyntaxKind.BarGreaterThanToken) {
return checkPipelineExpression(<PipelineExpression>node);
}
return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node);
}
@ -18029,6 +18077,12 @@ namespace ts {
}
}
function checkPipelineExpression(node: PipelineExpression) {
checkGrammarPipelineExpression(node);
const signature = getResolvedSignature(node);
return getReturnTypeOfSignature(signature);
}
function isYieldExpressionInClass(node: YieldExpression): boolean {
let current: Node = node;
let parent = node.parent;
@ -25282,6 +25336,12 @@ namespace ts {
}
}
function checkGrammarPipelineExpression(node: PipelineExpression) {
if (!compilerOptions.experimentalPipeline) {
return grammarErrorOnNode(node.operatorToken, Diagnostics.Pipeline_expressions_are_part_of_a_stage_1_ECMAScript_proposal_and_are_subject_to_change_in_future_releases_Set_the_experimentalPipeline_option_to_remove_this_warning);
}
}
function hasParseDiagnostics(sourceFile: SourceFile): boolean {
return sourceFile.parseDiagnostics.length > 0;
}

View File

@ -440,6 +440,12 @@ namespace ts {
category: Diagnostics.Experimental_Options,
description: Diagnostics.Enables_experimental_support_for_emitting_type_metadata_for_decorators
},
{
name: "experimentalPipeline",
type: "boolean",
category: Diagnostics.Experimental_Options,
description: Diagnostics.Enables_experimental_support_for_the_stage_1_ECMAScript_pipeline_operator
},
// Advanced
{

View File

@ -831,6 +831,11 @@
"category": "Error",
"code": 1254
},
"Pipeline expressions are part of a stage 1 ECMAScript proposal and are subject to change in future releases. Set the 'experimentalPipeline' option to remove this warning.": {
"category": "Error",
"code": 1255
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300
@ -3318,6 +3323,12 @@
"category": "Message",
"code": 6186
},
"Enables experimental support for the stage-1 ECMAScript pipeline operator.": {
"category": "Message",
"code": 6187
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005

View File

@ -3549,21 +3549,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:
@ -3571,20 +3573,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

View File

@ -168,6 +168,7 @@ namespace ts {
"~": SyntaxKind.TildeToken,
"&&": SyntaxKind.AmpersandAmpersandToken,
"||": SyntaxKind.BarBarToken,
"|>": SyntaxKind.BarGreaterThanToken,
"?": SyntaxKind.QuestionToken,
":": SyntaxKind.ColonToken,
"=": SyntaxKind.EqualsToken,
@ -1587,6 +1588,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:

View File

@ -225,6 +225,31 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
function transformPipelineExpressionWorker(node: PipelineExpression, pipelineVariable: Identifier, expressions: Expression[], top: boolean) {
if (isPipelineExpression(node.left)) {
transformPipelineExpressionWorker(node.left, pipelineVariable, expressions, false);
}
else {
expressions.push(createAssignment(pipelineVariable, visitNode(node.left, visitor, isExpression)));
}
const right = visitNode(node.right, visitor, isExpression);
const call = createCall(right, /*typeArguments*/ undefined, [pipelineVariable]);
setSourceMapRange(call, node);
setCommentRange(call, node);
expressions.push(top ? call : createAssignment(pipelineVariable, call));
}
function transformPipelineExpression(node: PipelineExpression) {
const pipelineVariable = createTempVariable(hoistVariableDeclaration);
const expressions: Expression[] = [];
transformPipelineExpressionWorker(node, pipelineVariable, expressions, /*top*/ true);
const transformed = inlineExpressions(expressions);
setSourceMapRange(transformed, node);
setCommentRange(transformed, node);
return transformed;
}
/**
* Visits a BinaryExpression that contains a destructuring assignment.
*
@ -247,6 +272,9 @@ namespace ts {
visitNode(node.right, noDestructuringValue ? visitorNoDestructuringValue : visitor, isExpression)
);
}
else if (node.operatorToken.kind === SyntaxKind.BarGreaterThanToken) {
return transformPipelineExpression(<PipelineExpression>node);
}
return visitEachChild(node, visitor, context);
}

View File

@ -109,6 +109,7 @@ namespace ts {
TildeToken,
AmpersandAmpersandToken,
BarBarToken,
BarGreaterThanToken,
QuestionToken,
ColonToken,
AtToken,
@ -1279,6 +1280,11 @@ namespace ts {
| LogicalOperator
;
export type PipelineOperatorOrHigher
= LogicalOperatorOrHigher
| SyntaxKind.BarGreaterThanToken
;
// see: https://tc39.github.io/ecma262/#prod-AssignmentOperator
export type CompoundAssignmentOperator
= SyntaxKind.PlusEqualsToken
@ -1303,7 +1309,7 @@ namespace ts {
// see: https://tc39.github.io/ecma262/#prod-AssignmentExpression
export type AssignmentOperatorOrHigher
= LogicalOperatorOrHigher
= PipelineOperatorOrHigher
| AssignmentOperator
;
@ -1315,13 +1321,18 @@ namespace ts {
export type BinaryOperatorToken = Token<BinaryOperator>;
export interface BinaryExpression extends Expression, Declaration {
export interface BinaryExpression extends Expression, Declaration {
kind: SyntaxKind.BinaryExpression;
left: Expression;
operatorToken: BinaryOperatorToken;
right: Expression;
}
/*@internal*/
export interface PipelineExpression extends BinaryExpression {
operatorToken: Token<SyntaxKind.BarGreaterThanToken>;
}
export type AssignmentOperatorToken = Token<AssignmentOperator>;
export interface AssignmentExpression<TOperator extends AssignmentOperatorToken> extends BinaryExpression {
@ -1581,7 +1592,7 @@ namespace ts {
template: TemplateLiteral;
}
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement;
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | PipelineExpression | JsxOpeningLikeElement;
export interface AsExpression extends Expression {
kind: SyntaxKind.AsExpression;
@ -3673,6 +3684,7 @@ namespace ts {
emitBOM?: boolean;
emitDecoratorMetadata?: boolean;
experimentalDecorators?: boolean;
experimentalPipeline?: boolean;
forceConsistentCasingInFileNames?: boolean;
/*@internal*/help?: boolean;
importHelpers?: boolean;

View File

@ -1106,7 +1106,7 @@ namespace ts {
}
}
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression {
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression | PipelineExpression | undefined {
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
let prev = func;
let parent = func.parent;
@ -1117,6 +1117,9 @@ namespace ts {
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
return parent as CallExpression;
}
if (isPipelineExpression(parent) && parent.right === prev) {
return parent;
}
}
}
@ -2155,49 +2158,49 @@ namespace ts {
case SyntaxKind.TemplateExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.OmittedExpression:
return 19;
return 20;
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
return 18;
return 19;
case SyntaxKind.NewExpression:
return hasArguments ? 18 : 17;
return hasArguments ? 19 : 18;
case SyntaxKind.CallExpression:
return 17;
return 18;
case SyntaxKind.PostfixUnaryExpression:
return 16;
return 17;
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.AwaitExpression:
return 15;
return 16;
case SyntaxKind.BinaryExpression:
switch (operatorKind) {
case SyntaxKind.ExclamationToken:
case SyntaxKind.TildeToken:
return 15;
return 16;
case SyntaxKind.AsteriskAsteriskToken:
case SyntaxKind.AsteriskToken:
case SyntaxKind.SlashToken:
case SyntaxKind.PercentToken:
return 14;
return 15;
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
return 13;
return 14;
case SyntaxKind.LessThanLessThanToken:
case SyntaxKind.GreaterThanGreaterThanToken:
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
return 12;
return 13;
case SyntaxKind.LessThanToken:
case SyntaxKind.LessThanEqualsToken:
@ -2205,27 +2208,30 @@ namespace ts {
case SyntaxKind.GreaterThanEqualsToken:
case SyntaxKind.InKeyword:
case SyntaxKind.InstanceOfKeyword:
return 11;
return 12;
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
return 10;
return 11;
case SyntaxKind.AmpersandToken:
return 9;
return 10;
case SyntaxKind.CaretToken:
return 8;
return 9;
case SyntaxKind.BarToken:
return 7;
return 8;
case SyntaxKind.AmpersandAmpersandToken:
return 6;
return 7;
case SyntaxKind.BarBarToken:
return 6;
case SyntaxKind.BarGreaterThanToken:
return 5;
case SyntaxKind.EqualsToken:
@ -4520,6 +4526,11 @@ namespace ts {
return node.kind === SyntaxKind.BinaryExpression;
}
export function isPipelineExpression(node: Node): node is PipelineExpression {
return isBinaryExpression(node)
&& node.operatorToken.kind === SyntaxKind.BarGreaterThanToken;
}
export function isConditionalExpression(node: Node): node is ConditionalExpression {
return node.kind === SyntaxKind.ConditionalExpression;
}

View File

@ -1549,6 +1549,14 @@ namespace ts {
assertOptionalNode)
: noop;
export const assertToken = shouldAssert(AssertionLevel.Normal)
? (node: Node, kind: SyntaxKind, message?: string): void => assert(
kind === undefined || node.kind === kind,
message || "Unexpected node.",
() => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
assertOptionalToken)
: noop;
export const assertOptionalToken = shouldAssert(AssertionLevel.Normal)
? (node: Node, kind: SyntaxKind, message?: string): void => assert(
kind === undefined || node === undefined || node.kind === kind,