Skip to content

Commit 80b8062

Browse files
Addressed CR feedback.
1 parent 652b7f8 commit 80b8062

File tree

1 file changed

+81
-47
lines changed

1 file changed

+81
-47
lines changed

src/compiler/checker.ts

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
/// <reference path="emitter.ts"/>
77

88
module ts {
9-
109
var nextSymbolId = 1;
1110
var nextNodeId = 1;
1211
var nextMergeId = 1;
@@ -57,6 +56,8 @@ module ts {
5756
return stringWriters.pop();
5857
}
5958

59+
type CallLikeExpression = CallExpression | TaggedTemplateExpression;
60+
6061
/// fullTypeCheck denotes if this instance of the typechecker will be used to get semantic diagnostics.
6162
/// If fullTypeCheck === true, then the typechecker should do every possible check to produce all errors
6263
/// If fullTypeCheck === false, the typechecker can take shortcuts and skip checks that only produce errors.
@@ -5183,7 +5184,7 @@ module ts {
51835184
return unknownType;
51845185
}
51855186

5186-
function resolveUntypedCall(node: CallExpression | TaggedTemplateExpression): Signature {
5187+
function resolveUntypedCall(node: CallLikeExpression): Signature {
51875188
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
51885189
checkExpression((<TaggedTemplateExpression>node).template);
51895190
}
@@ -5195,52 +5196,78 @@ module ts {
51955196
return anySignature;
51965197
}
51975198

5198-
function resolveErrorCall(node: CallExpression | TaggedTemplateExpression): Signature {
5199+
function resolveErrorCall(node: CallLikeExpression): Signature {
51995200
resolveUntypedCall(node);
52005201
return unknownSignature;
52015202
}
52025203

5203-
function signatureHasCorrectArity(node: CallExpression | TaggedTemplateExpression, args: Expression[], signature: Signature): boolean {
5204-
var isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
5204+
function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature) {
5205+
var adjustedArgCount: number;
5206+
var typeArguments: NodeArray<TypeNode>;
5207+
var callIsIncomplete = false;
52055208

5206-
if (!isTaggedTemplate && !(<CallExpression>node).arguments) {
5207-
// This only happens when we have something of the form:
5208-
// new C
5209-
//
5210-
Debug.assert(node.kind === SyntaxKind.NewExpression);
5211-
return signature.minArgumentCount === 0;
5212-
}
5213-
5214-
// For IDE scenarios, since we may have an incomplete call, we make two modifications
5215-
// to arity checking.
5216-
// 1. A trailing comma is tantamount to adding another argument
5217-
// 2. If the call is incomplete (no closing paren) allow fewer arguments than expected
5218-
var numberOfArgs = !isTaggedTemplate && (<CallExpression>node).arguments.hasTrailingComma
5219-
? args.length + 1
5220-
: args.length;
5221-
var hasTooManyArguments = !signature.hasRestParameter && numberOfArgs > signature.parameters.length;
5222-
var hasRightNumberOfTypeArguments = !(<CallExpression>node).typeArguments ||
5223-
(signature.typeParameters && (<CallExpression>node).typeArguments.length === signature.typeParameters.length);
5224-
5225-
if (hasTooManyArguments || !hasRightNumberOfTypeArguments) {
5226-
return false;
5227-
}
5209+
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
5210+
var tagExpression = <TaggedTemplateExpression>node;
52285211

5229-
// If we are missing the close paren, the call is incomplete, and we should skip
5230-
// the lower bound check.
5231-
var callIsIncomplete = false;
5232-
if (isTaggedTemplate) {
5233-
var template = (<TaggedTemplateExpression>node).template;
5234-
if (template.kind === SyntaxKind.TemplateExpression) {
5235-
var lastSpan = lastOrUndefined((<TemplateExpression>template).templateSpans)
5212+
adjustedArgCount = args.length;
5213+
typeArguments = undefined;
5214+
5215+
if (tagExpression.kind === SyntaxKind.TemplateExpression) {
5216+
// If a tagged template expression lacks a tail literal, the call is incomplete.
5217+
var template = <TemplateExpression>tagExpression.template;
5218+
var lastSpan = lastOrUndefined(template.templateSpans);
5219+
Debug.assert(lastSpan !== undefined); // we should always have at least one span.
52365220
callIsIncomplete = lastSpan === undefined || lastSpan.literal.kind !== SyntaxKind.TemplateTail;
52375221
}
52385222
}
52395223
else {
5240-
callIsIncomplete = (<CallExpression>node).arguments.end === node.end;
5224+
var callExpression = <CallExpression>node;
5225+
if (!callExpression.arguments) {
5226+
// This only happens when we have something of the form: 'new C'
5227+
Debug.assert(callExpression.kind === SyntaxKind.NewExpression);
5228+
5229+
return signature.minArgumentCount === 0;
5230+
}
5231+
else {
5232+
// For IDE scenarios we may have an incomplete call, so a trailing comma is tantamount to adding another argument.
5233+
adjustedArgCount = callExpression.arguments.hasTrailingComma ? args.length + 1 : args.length;
5234+
5235+
// If we are missing the close paren, the call is incomplete.
5236+
callIsIncomplete = (<CallExpression>callExpression).arguments.end === callExpression.end;
5237+
}
5238+
5239+
typeArguments = callExpression.typeArguments;
5240+
}
5241+
5242+
return checkArity(adjustedArgCount, typeArguments, callIsIncomplete, signature);
5243+
5244+
/**
5245+
* @param adjustedArgCount The "apparent" number of arguments that we will have in this call.
5246+
* @param typeArguments Type arguments node of the call if it exists; undefined otherwise.
5247+
* @param callIsIncomplete Whether or not a call is unfinished, and we should be "lenient" when we have too few arguments.
5248+
* @param signature The signature whose arity we are comparing.
5249+
*/
5250+
function checkArity(adjustedArgCount: number,
5251+
typeArguments: NodeArray<TypeNode>,
5252+
callIsIncomplete: boolean,
5253+
signature: Signature): boolean {
5254+
// Too many arguments implies incorrect arity.
5255+
if (!signature.hasRestParameter && adjustedArgCount > signature.parameters.length) {
5256+
return false;
5257+
}
5258+
5259+
// If the user supplied type arguments, but the number of type arguments does not match
5260+
// the declared number of type parameters, the call has an incorrect arity.
5261+
var hasRightNumberOfTypeArgs = !typeArguments ||
5262+
(signature.typeParameters && typeArguments.length === signature.typeParameters.length);
5263+
if (!hasRightNumberOfTypeArgs) {
5264+
return false;
5265+
}
5266+
5267+
// If the call is incomplete, we should skip the lower bound check.
5268+
var hasEnoughArguments = adjustedArgCount >= signature.minArgumentCount;
5269+
return callIsIncomplete || hasEnoughArguments;
52415270
}
5242-
var hasEnoughArguments = numberOfArgs >= signature.minArgumentCount;
5243-
return callIsIncomplete || hasEnoughArguments;
52445271
}
52455272

52465273
// If type has a single call signature and no other members, return that signature. Otherwise, return undefined.
@@ -5332,7 +5359,7 @@ module ts {
53325359
return typeArgumentsAreAssignable;
53335360
}
53345361

5335-
function checkApplicableSignature(node: CallExpression | TaggedTemplateExpression, args: Node[], signature: Signature, relation: Map<Ternary>, excludeArgument: boolean[], reportErrors: boolean) {
5362+
function checkApplicableSignature(node: CallLikeExpression, args: Node[], signature: Signature, relation: Map<Ternary>, excludeArgument: boolean[], reportErrors: boolean) {
53365363
for (var i = 0; i < args.length; i++) {
53375364
var arg = args[i];
53385365
var argType: Type;
@@ -5371,16 +5398,18 @@ module ts {
53715398
*
53725399
* If 'node' is a CallExpression or a NewExpression, then its argument list is returned.
53735400
* If 'node' is a TaggedTemplateExpression, a new argument list is constructed from the substitution
5374-
* expressions, where the first element of the argument list is the template portion for error reporting purposes.
5401+
* expressions, where the first element of the list is the template for error reporting purposes.
53755402
*/
5376-
function getEffectiveCallArguments(node: CallExpression | TaggedTemplateExpression): Expression[] {
5403+
function getEffectiveCallArguments(node: CallLikeExpression): Expression[] {
53775404
var args: Expression[];
53785405
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
53795406
var template = (<TaggedTemplateExpression>node).template;
53805407
args = [template];
53815408

53825409
if (template.kind === SyntaxKind.TemplateExpression) {
5383-
args.push.apply(args, map((<TemplateExpression>template).templateSpans, span => span.expression));
5410+
forEach((<TemplateExpression>template).templateSpans, span => {
5411+
args.push(span.expression);
5412+
});
53845413
}
53855414
}
53865415
else {
@@ -5390,7 +5419,7 @@ module ts {
53905419
return args;
53915420
}
53925421

5393-
function resolveCall(node: CallExpression | TaggedTemplateExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature {
5422+
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature {
53945423
var typeArguments = (<CallExpression>node).typeArguments;
53955424
forEach(typeArguments, checkSourceElement);
53965425

@@ -5410,7 +5439,12 @@ module ts {
54105439
// - undefined: the argument at 'i' is *not* susceptible to permanent contextual typing.
54115440
// - false: the argument at 'i' *was* and *has been* permanently contextually typed.
54125441
//
5413-
// If the expression is a tagged template, then the first argument is implicitly the "cooked" strings array.
5442+
// The idea is that we will perform type argument inference & assignability checking once
5443+
// without using the susceptible parameters, and once more for each susceptible parameter,
5444+
// contextually typing each as we go along.
5445+
//
5446+
// For a tagged template, then the first argument be 'undefined' if necessary
5447+
// because it represents a TemplateStringsArray.
54145448
var excludeArgument: boolean[];
54155449
for (var i = isTaggedTemplate ? 1 : 0; i < args.length; i++) {
54165450
if (isContextSensitiveExpression(args[i])) {
@@ -5484,7 +5518,7 @@ module ts {
54845518
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
54855519
}
54865520
else if (candidateForTypeArgumentError) {
5487-
if ((<CallExpression>node).typeArguments) {
5521+
if (!isTaggedTemplate && (<CallExpression>node).typeArguments) {
54885522
checkTypeArguments(candidateForTypeArgumentError, (<CallExpression>node).typeArguments, [], /*reportErrors*/ true)
54895523
}
54905524
else {
@@ -5510,7 +5544,7 @@ module ts {
55105544
// f({ |
55115545
if (!fullTypeCheck) {
55125546
for (var i = 0, n = candidates.length; i < n; i++) {
5513-
if (signatureHasCorrectArity(node, args, candidates[i])) {
5547+
if (hasCorrectArity(node, args, candidates[i])) {
55145548
return candidates[i];
55155549
}
55165550
}
@@ -5520,7 +5554,7 @@ module ts {
55205554

55215555
function chooseOverload(candidates: Signature[], relation: Map<Ternary>, excludeArgument: boolean[]) {
55225556
for (var i = 0; i < candidates.length; i++) {
5523-
if (!signatureHasCorrectArity(node, args, candidates[i])) {
5557+
if (!hasCorrectArity(node, args, candidates[i])) {
55245558
continue;
55255559
}
55265560

@@ -5751,7 +5785,7 @@ module ts {
57515785

57525786
// candidatesOutArray is passed by signature help in the language service, and collectCandidates
57535787
// must fill it up with the appropriate candidate signatures
5754-
function getResolvedSignature(node: CallExpression | TaggedTemplateExpression, candidatesOutArray?: Signature[]): Signature {
5788+
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
57555789
var links = getNodeLinks(node);
57565790
// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
57575791
// However, it is possible that either candidatesOutArray was not passed in the first time,

0 commit comments

Comments
 (0)