Type check for bind, polish emit for pipeline

This commit is contained in:
Ron Buckton
2016-11-22 01:12:20 -05:00
parent c1c7e20856
commit aebb806955
4 changed files with 136 additions and 66 deletions
+78 -17
View File
@@ -4304,6 +4304,17 @@ namespace ts {
sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasLiteralTypes);
}
function getSignatureWithoutThis(sig: Signature) {
if (sig.thisParameter) {
if (!sig.thisFreeSignatureCache) {
sig.thisFreeSignatureCache = cloneSignature(sig);
sig.thisFreeSignatureCache.thisParameter = undefined;
}
return sig.thisFreeSignatureCache;
}
return sig;
}
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
@@ -5195,6 +5206,25 @@ namespace ts {
return signature.erasedSignatureCache;
}
function createTypeFromSignatures(signatures: Signature[]): ObjectType {
const type = <ResolvedType>createObjectType(ObjectFlags.Anonymous);
type.members = emptySymbols;
type.properties = emptyArray;
let callSignatures: Signature[];
let constructSignatures: Signature[];
for (const signature of signatures) {
if (signature.isConstruct) {
constructSignatures = append(constructSignatures, signature);
}
else {
callSignatures = append(callSignatures, signature);
}
}
type.callSignatures = callSignatures || emptyArray;
type.constructSignatures = constructSignatures || emptyArray;
return type;
}
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
// There are two ways to declare a construct signature, one is by declaring a class constructor
// using the constructor keyword, and the other is declaring a bare construct signature in an
@@ -12186,6 +12216,12 @@ namespace ts {
return getIndexedAccessType(objectType, indexType, node);
}
function checkBindExpression(node: BindExpression): Type {
checkNonNullExpression(node.expression);
const signatures = getResolvedPartialSignatures(node);
return createTypeFromSignatures(map(signatures, getSignatureWithoutThis));
}
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
if (expressionType === unknownType) {
// There is already an error, so no need to report one.
@@ -12232,7 +12268,7 @@ namespace ts {
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
checkExpression((<TaggedTemplateExpression>node).template);
}
else if (node.kind !== SyntaxKind.Decorator) {
else if (node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression) {
forEach((<CallExpression>node).arguments, argument => {
checkExpression(argument);
});
@@ -12313,7 +12349,6 @@ namespace ts {
let argCount: number; // Apparent number of arguments we will have in this call
let typeArguments: NodeArray<TypeNode>; // Type arguments (undefined if none)
let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments
let isDecorator: boolean;
let spreadArgIndex = -1;
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
@@ -12341,8 +12376,7 @@ namespace ts {
callIsIncomplete = !!templateLiteral.isUnterminated;
}
}
else if (node.kind === SyntaxKind.Decorator) {
isDecorator = true;
else if (node.kind === SyntaxKind.Decorator || node.kind === SyntaxKind.BindExpression) {
typeArguments = undefined;
argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
}
@@ -12574,6 +12608,9 @@ namespace ts {
return (callee as ElementAccessExpression).expression;
}
}
else if (node.kind === SyntaxKind.BindExpression) {
return node.expression;
}
}
/**
@@ -12596,7 +12633,7 @@ namespace ts {
});
}
}
else if (node.kind === SyntaxKind.Decorator) {
else if (node.kind === SyntaxKind.Decorator || node.kind === SyntaxKind.BindExpression) {
// For a decorator, we return undefined as we will determine
// the number and types of arguments for a decorator using
// `getEffectiveArgumentCount` and `getEffectiveArgumentType` below.
@@ -12675,6 +12712,9 @@ namespace ts {
return 3;
}
}
else if (node.kind === SyntaxKind.BindExpression) {
return signature.minArgumentCount;
}
else {
return args.length;
}
@@ -12859,6 +12899,9 @@ namespace ts {
if (node.kind === SyntaxKind.Decorator) {
return getEffectiveDecoratorArgumentType(<Decorator>node, argIndex);
}
else if (node.kind === SyntaxKind.BindExpression) {
return unknownType;
}
else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) {
return getGlobalTemplateStringsArrayType();
}
@@ -12874,6 +12917,7 @@ namespace ts {
function getEffectiveArgument(node: CallLikeExpression, args: Expression[], argIndex: number) {
// For a decorator or the first argument of a tagged template expression we return undefined.
if (node.kind === SyntaxKind.Decorator ||
node.kind === SyntaxKind.BindExpression ||
(argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression)) {
return undefined;
}
@@ -12902,10 +12946,11 @@ namespace ts {
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
const isDecorator = node.kind === SyntaxKind.Decorator;
const isPipeline = node.kind === SyntaxKind.BinaryExpression;
const isBind = node.kind === SyntaxKind.BindExpression;
let typeArguments: TypeNode[];
if (!isTaggedTemplate && !isDecorator && !isPipeline) {
if (!isTaggedTemplate && !isDecorator && !isPipeline && !isBind) {
typeArguments = (<CallExpression>node).typeArguments;
// We already perform checking on the type arguments on the class declaration itself.
@@ -12939,7 +12984,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.
let excludeArgument: boolean[];
if (!isDecorator) {
if (!isDecorator && !isBind) {
// We do not need to call `getEffectiveArgumentCount` here as it only
// applies when calculating the number of arguments for a decorator.
for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) {
@@ -13420,7 +13465,6 @@ namespace ts {
}
function resolvePipelineExpression(node: PipelineExpression, candidatesOutArray: Signature[]): Signature {
(<any>Error).stackTraceLimit = Infinity;
const funcType = checkExpression(node.right);
const apparentType = getApparentType(funcType);
if (apparentType === unknownType) {
@@ -13440,6 +13484,26 @@ namespace ts {
return resolveCall(node, callSignatures, /*partialSignaturesOutArray*/ undefined, candidatesOutArray);
}
function resolveBindExpression(node: BindExpression, partialSignaturesOutArray: Signature[], candidatesOutArray?: Signature[]): Signature {
const funcType = checkExpression(node.targetExpression);
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, partialSignaturesOutArray, candidatesOutArray);
}
function resolveSignature(node: CallLikeExpression, partialSignaturesOutArray: Signature[], candidatesOutArray?: Signature[]): Signature {
switch (node.kind) {
case SyntaxKind.CallExpression:
@@ -13452,6 +13516,8 @@ namespace ts {
return resolveDecorator(<Decorator>node, candidatesOutArray);
case SyntaxKind.BinaryExpression:
return resolvePipelineExpression(<PipelineExpression>node, candidatesOutArray);
case SyntaxKind.BindExpression:
return resolveBindExpression(<BindExpression>node, partialSignaturesOutArray, candidatesOutArray);
}
Debug.fail("Branch in 'resolveSignature' should be unreachable.");
}
@@ -14238,19 +14304,12 @@ namespace ts {
function createOperatorExpressionType(operator: SyntaxKind) {
switch (operator) {
case SyntaxKind.PlusToken:
const signatures = [
return createTypeFromSignatures([
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);
(<ResolvedType>plusType).members = emptySymbols;
(<ResolvedType>plusType).properties = emptyArray;
(<ResolvedType>plusType).callSignatures = signatures;
(<ResolvedType>plusType).constructSignatures = emptyArray;
return plusType;
]);
case SyntaxKind.AsteriskToken:
case SyntaxKind.AsteriskAsteriskToken:
case SyntaxKind.SlashToken:
@@ -15249,6 +15308,8 @@ namespace ts {
return checkPropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return checkIndexedAccess(<ElementAccessExpression>node);
case SyntaxKind.BindExpression:
return checkBindExpression(<BindExpression>node);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return checkCallExpression(<CallExpression>node);
+3
View File
@@ -161,6 +161,9 @@ namespace ts {
case SyntaxKind.ElementAccessExpression:
return visitNode(cbNode, (<ElementAccessExpression>node).expression) ||
visitNode(cbNode, (<ElementAccessExpression>node).argumentExpression);
case SyntaxKind.BindExpression:
return visitNode(cbNode, (<BindExpression>node).expression) ||
visitNode(cbNode, (<BindExpression>node).targetExpression);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return visitNode(cbNode, (<CallExpression>node).expression) ||
+52 -48
View File
@@ -5,9 +5,9 @@
namespace ts {
export function transformESNext(context: TransformationContext) {
const {
startLexicalEnvironment,
resumeLexicalEnvironment,
endLexicalEnvironment
endLexicalEnvironment,
hoistVariableDeclaration,
} = context;
return transformSourceFile;
@@ -441,60 +441,64 @@ namespace ts {
}
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 = <Identifier>parameter.name;
position++;
}
else if (isPositionalSpreadElement(argument)) {
const parameter = positionalRestParameter || (positionalRestParameter = createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, createToken(SyntaxKind.DotDotDotToken)));
updated = createSpreadElement(<Identifier>parameter.name);
}
else {
updated = visitNode(argument, visitor, isExpression);
}
if (argumentList || updated !== argument) {
if (!argumentList) {
argumentList = node.arguments.slice(0, i);
}
argumentList.push(updated);
}
let expression = visitNode(node.expression, visitor, isExpression) as Expression;
if (!forEach(node.arguments, isPositionalOrPositionalSpreadElement)) {
return updateCall(
node,
expression,
/*typeArguments*/ undefined,
visitNodes(node.arguments, visitor, isExpression));
}
if (positionalParameters || positionalRestParameter) {
startLexicalEnvironment();
else {
const expressionTemp = createTempVariable(hoistVariableDeclaration);
const argumentList: Expression[] = [];
const pendingExpressions: Expression[] = [createAssignment(expressionTemp, expression)];
const positionalParameters: ParameterDeclaration[] = [];
let positionalRestParameter: ParameterDeclaration;
let position = 0;
for (let i = 0; i < node.arguments.length; i++) {
let argument = node.arguments[i];
if (isPositionalElement(argument)) {
if (argument.literal) {
position = +argument.literal.text;
}
const parameter = positionalParameters[position] || (positionalParameters[position] = createParameter());
argument = <Identifier>parameter.name;
position++;
}
else if (isPositionalSpreadElement(argument)) {
const parameter = positionalRestParameter || (positionalRestParameter = createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, createToken(SyntaxKind.DotDotDotToken)));
argument = createSpreadElement(<Identifier>parameter.name);
}
else {
argument = visitNode(argument, visitor, isExpression);
const temp = createTempVariable(hoistVariableDeclaration);
pendingExpressions.push(createAssignment(temp, isSpreadElement(argument) ? argument.expression : argument));
argument = isSpreadElement(argument) ? createSpreadElement(temp) : temp;
}
argumentList.push(argument);
}
if (positionalRestParameter) {
positionalParameters = append(positionalParameters, positionalRestParameter);
positionalParameters.push(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 || node.arguments),
/*location*/ node
pendingExpressions.push(
createArrowFunction(
/*modifiers*/ undefined,
/*typeParameters*/ undefined,
positionalParameters,
/*type*/ undefined,
/*equalsGreaterThanToken*/ createToken(SyntaxKind.EqualsGreaterThanToken),
updateCall(node, expressionTemp, /*typeArguments*/ undefined, argumentList),
/*location*/ node
)
);
return inlineExpressions(pendingExpressions);
}
return updateCall(node, expression, /*typeArguments*/ undefined, argumentList || node.arguments);
}
function visitOperatorExpression(node: OperatorExpression) {
@@ -542,7 +546,7 @@ namespace ts {
function visitBindExpression(node: BindExpression) {
const thisArg = createTempVariable(context.hoistVariableDeclaration);
return inlineExpressions([
return createComma(
createAssignment(
thisArg,
visitNode(node.expression, visitor, isExpression),
@@ -554,7 +558,7 @@ namespace ts {
[],
node
)
]);
);
}
}
+3 -1
View File
@@ -1456,7 +1456,7 @@ namespace ts {
template: TemplateLiteral;
}
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | PipelineExpression;
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | PipelineExpression | BindExpression;
export interface PositionalElement extends Expression {
kind: SyntaxKind.PositionalElement;
@@ -3053,6 +3053,8 @@ namespace ts {
/* @internal */
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
/* @internal */
thisFreeSignatureCache?: Signature; // Signature without 'this' parameter.
/* @internal */
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
/* @internal */
typePredicate?: TypePredicate;