Skip to content

Commit 986a0b7

Browse files
Fixed issues with incomplete calls, cleaned some code up.
1 parent 80b8062 commit 986a0b7

19 files changed

+218
-24
lines changed

src/compiler/checker.ts

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ module ts {
5656
return stringWriters.pop();
5757
}
5858

59-
type CallLikeExpression = CallExpression | TaggedTemplateExpression;
60-
6159
/// fullTypeCheck denotes if this instance of the typechecker will be used to get semantic diagnostics.
6260
/// If fullTypeCheck === true, then the typechecker should do every possible check to produce all errors
6361
/// If fullTypeCheck === false, the typechecker can take shortcuts and skip checks that only produce errors.
@@ -5204,20 +5202,30 @@ module ts {
52045202
function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature) {
52055203
var adjustedArgCount: number;
52065204
var typeArguments: NodeArray<TypeNode>;
5207-
var callIsIncomplete = false;
5205+
var callIsIncomplete: boolean;
52085206

52095207
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
52105208
var tagExpression = <TaggedTemplateExpression>node;
52115209

5210+
// Even if the call is incomplete, we'll have a missing expression as our last argument,
5211+
// so we can say the count is just the arg list length
52125212
adjustedArgCount = args.length;
52135213
typeArguments = undefined;
52145214

5215-
if (tagExpression.kind === SyntaxKind.TemplateExpression) {
5215+
if (tagExpression.template.kind === SyntaxKind.TemplateExpression) {
52165216
// 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);
5217+
// Specifically, a template only can end in a TemplateTail or a Missing literal.
5218+
var templateExpression = <TemplateExpression>tagExpression.template;
5219+
var lastSpan = lastOrUndefined(templateExpression.templateSpans);
52195220
Debug.assert(lastSpan !== undefined); // we should always have at least one span.
5220-
callIsIncomplete = lastSpan === undefined || lastSpan.literal.kind !== SyntaxKind.TemplateTail;
5221+
callIsIncomplete = lastSpan.literal.kind === SyntaxKind.Missing || isUnterminatedTemplateEnd(lastSpan.literal);
5222+
}
5223+
else {
5224+
// If the template didn't end in a backtick, or its beginning occurred right prior to EOF,
5225+
// then this might actually turn out to be a TemplateHead in the future;
5226+
// so we consider the call to be incomplete.
5227+
var templateLiteral = <LiteralExpression>tagExpression.template;
5228+
callIsIncomplete = isUnterminatedTemplateEnd(templateLiteral);
52215229
}
52225230
}
52235231
else {
@@ -5228,17 +5236,17 @@ module ts {
52285236

52295237
return signature.minArgumentCount === 0;
52305238
}
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;
5239+
5240+
// For IDE scenarios we may have an incomplete call, so a trailing comma is tantamount to adding another argument.
5241+
adjustedArgCount = callExpression.arguments.hasTrailingComma ? args.length + 1 : args.length;
52345242

5235-
// If we are missing the close paren, the call is incomplete.
5236-
callIsIncomplete = (<CallExpression>callExpression).arguments.end === callExpression.end;
5237-
}
5243+
// If we are missing the close paren, the call is incomplete.
5244+
callIsIncomplete = (<CallExpression>callExpression).arguments.end === callExpression.end;
52385245

52395246
typeArguments = callExpression.typeArguments;
52405247
}
52415248

5249+
52425250
return checkArity(adjustedArgCount, typeArguments, callIsIncomplete, signature);
52435251

52445252
/**
@@ -5420,7 +5428,9 @@ module ts {
54205428
}
54215429

54225430
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature {
5423-
var typeArguments = (<CallExpression>node).typeArguments;
5431+
var isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
5432+
5433+
var typeArguments = isTaggedTemplate ? undefined : (<CallExpression>node).typeArguments;
54245434
forEach(typeArguments, checkSourceElement);
54255435

54265436
var candidates = candidatesOutArray || [];
@@ -5432,16 +5442,15 @@ module ts {
54325442
}
54335443

54345444
var args = getEffectiveCallArguments(node);
5435-
var isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
54365445

54375446
// The following applies to any value of 'excludeArgument[i]':
54385447
// - true: the argument at 'i' is susceptible to a one-time permanent contextual typing.
54395448
// - undefined: the argument at 'i' is *not* susceptible to permanent contextual typing.
54405449
// - false: the argument at 'i' *was* and *has been* permanently contextually typed.
54415450
//
54425451
// 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.
5452+
// without using the susceptible parameters that are functions, and once more for each of those
5453+
// parameters, contextually typing each as we go along.
54455454
//
54465455
// For a tagged template, then the first argument be 'undefined' if necessary
54475456
// because it represents a TemplateStringsArray.
@@ -5492,14 +5501,14 @@ module ts {
54925501
// is just important for choosing the best signature. So in the case where there is only one
54935502
// signature, the subtype pass is useless. So skipping it is an optimization.
54945503
if (candidates.length > 1) {
5495-
result = chooseOverload(candidates, subtypeRelation, excludeArgument);
5504+
result = chooseOverload(candidates, subtypeRelation);
54965505
}
54975506
if (!result) {
54985507
// Reinitialize these pointers for round two
54995508
candidateForArgumentError = undefined;
55005509
candidateForTypeArgumentError = undefined;
55015510
resultOfFailedInference = undefined;
5502-
result = chooseOverload(candidates, assignableRelation, excludeArgument);
5511+
result = chooseOverload(candidates, assignableRelation);
55035512
}
55045513
if (result) {
55055514
return result;
@@ -5552,7 +5561,7 @@ module ts {
55525561

55535562
return resolveErrorCall(node);
55545563

5555-
function chooseOverload(candidates: Signature[], relation: Map<Ternary>, excludeArgument: boolean[]) {
5564+
function chooseOverload(candidates: Signature[], relation: Map<Ternary>) {
55565565
for (var i = 0; i < candidates.length; i++) {
55575566
if (!hasCorrectArity(node, args, candidates[i])) {
55585567
continue;
@@ -5566,9 +5575,9 @@ module ts {
55665575
if (candidate.typeParameters) {
55675576
var typeArgumentTypes: Type[];
55685577
var typeArgumentsAreValid: boolean;
5569-
if ((<CallExpression>node).typeArguments) {
5578+
if (typeArguments) {
55705579
typeArgumentTypes = new Array<Type>(candidate.typeParameters.length);
5571-
typeArgumentsAreValid = checkTypeArguments(candidate, (<CallExpression>node).typeArguments, typeArgumentTypes, /*reportErrors*/ false)
5580+
typeArgumentsAreValid = checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)
55725581
}
55735582
else {
55745583
inferenceResult = inferTypeArguments(candidate, args, excludeArgument);
@@ -5602,7 +5611,7 @@ module ts {
56025611
}
56035612
else {
56045613
candidateForTypeArgumentError = originalCandidate;
5605-
if (!(<CallExpression>node).typeArguments) {
5614+
if (!typeArguments) {
56065615
resultOfFailedInference = inferenceResult;
56075616
}
56085617
}

src/compiler/parser.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,6 @@ module ts {
564564
return false;
565565
}
566566

567-
568567
export function isDeclaration(node: Node): boolean {
569568
switch (node.kind) {
570569
case SyntaxKind.TypeParameter:
@@ -752,6 +751,20 @@ module ts {
752751
return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
753752
}
754753

754+
export function isUnterminatedTemplateEnd(node: LiteralExpression) {
755+
Debug.assert(node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.TemplateTail);
756+
var sourceText = getSourceFileOfNode(node).text;
757+
758+
// If we're not at the EOF, we know we must be terminated.
759+
if (node.end !== sourceText.length) {
760+
return false;
761+
}
762+
763+
// If we didn't end in a backtick, we must still be in the middle of a template.
764+
// If we did, make sure that it's not the *initial* backtick.
765+
return sourceText.charCodeAt(node.end - 1) !== CharacterCodes.backtick || node.text.length === 0;
766+
}
767+
755768
export function isModifier(token: SyntaxKind): boolean {
756769
switch (token) {
757770
case SyntaxKind.PublicKeyword:

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,8 @@ module ts {
445445
template: LiteralExpression | TemplateExpression;
446446
}
447447

448+
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression;
449+
448450
export interface TypeAssertion extends Expression {
449451
type: TypeNode;
450452
operand: Expression;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
tests/cases/compiler/taggedTemplatesWithIncompleteNoSubstitutionTemplate1.ts(6,15): error TS1126: Unexpected end of text.
2+
3+
4+
==== tests/cases/compiler/taggedTemplatesWithIncompleteNoSubstitutionTemplate1.ts (1 errors) ====
5+
6+
function f(x: TemplateStringsArray, y: string, z: string) {
7+
}
8+
9+
// Incomplete call, not enough parameters.
10+
f `123qdawdrqw
11+
12+
!!! error TS1126: Unexpected end of text.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
tests/cases/compiler/taggedTemplatesWithIncompleteNoSubstitutionTemplate2.ts(6,4): error TS1126: Unexpected end of text.
2+
3+
4+
==== tests/cases/compiler/taggedTemplatesWithIncompleteNoSubstitutionTemplate2.ts (1 errors) ====
5+
6+
function f(x: TemplateStringsArray, y: string, z: string) {
7+
}
8+
9+
// Incomplete call, not enough parameters, at EOF.
10+
f `
11+
12+
!!! error TS1126: Unexpected end of text.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions1.ts(6,17): error TS1109: Expression expected.
2+
3+
4+
==== tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions1.ts (1 errors) ====
5+
6+
function f(x: TemplateStringsArray, y: string, z: string) {
7+
}
8+
9+
// Incomplete call, not enough parameters.
10+
f `123qdawdrqw${
11+
12+
!!! error TS1109: Expression expected.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions2.ts(6,18): error TS1109: Expression expected.
2+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions2.ts(6,21): error TS1109: Expression expected.
3+
4+
5+
==== tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions2.ts (2 errors) ====
6+
7+
function f(x: TemplateStringsArray, y: string, z: string) {
8+
}
9+
10+
// Incomplete call, enough parameters.
11+
f `123qdawdrqw${ }${
12+
~
13+
!!! error TS1109: Expression expected.
14+
15+
!!! error TS1109: Expression expected.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions3.ts(6,23): error TS1109: Expression expected.
2+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions3.ts(6,18): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
3+
4+
5+
==== tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions3.ts (2 errors) ====
6+
7+
function f(x: TemplateStringsArray, y: string, z: string) {
8+
}
9+
10+
// Incomplete call, not enough parameters.
11+
f `123qdawdrqw${ 1 }${
12+
13+
!!! error TS1109: Expression expected.
14+
~
15+
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions4.ts(6,24): error TS1109: Expression expected.
2+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions4.ts(6,28): error TS1109: Expression expected.
3+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions4.ts(6,1): error TS2346: Supplied parameters do not match any signature of call target.
4+
5+
6+
==== tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions4.ts (3 errors) ====
7+
8+
function f(x: TemplateStringsArray, y: string, z: string) {
9+
}
10+
11+
// Incomplete call, but too many parameters.
12+
f `123qdawdrqw${ 1 }${ }${
13+
~
14+
!!! error TS1109: Expression expected.
15+
16+
!!! error TS1109: Expression expected.
17+
~~~~~~~~~~~~~~~~~~~~~~~~~~
18+
!!! error TS2346: Supplied parameters do not match any signature of call target.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions5.ts(6,30): error TS1109: Expression expected.
2+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions5.ts(6,1): error TS2346: Supplied parameters do not match any signature of call target.
3+
4+
5+
==== tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions5.ts (2 errors) ====
6+
7+
function f(x: TemplateStringsArray, y: string, z: string) {
8+
}
9+
10+
// Incomplete call, but too many parameters.
11+
f `123qdawdrqw${ 1 }${ 2 }${
12+
13+
!!! error TS1109: Expression expected.
14+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15+
!!! error TS2346: Supplied parameters do not match any signature of call target.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions6.ts(6,23): error TS1109: Expression expected.
2+
tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions6.ts(6,18): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
3+
4+
5+
==== tests/cases/compiler/taggedTemplatesWithIncompleteTemplateExpressions6.ts (2 errors) ====
6+
7+
function f(x: TemplateStringsArray, y: string, z: string) {
8+
}
9+
10+
// Incomplete call, not enough parameters, at EOF.
11+
f `123qdawdrqw${ 1 }${
12+
13+
!!! error TS1109: Expression expected.
14+
~
15+
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es6
2+
3+
function f(x: TemplateStringsArray, y: string, z: string) {
4+
}
5+
6+
// Incomplete call, not enough parameters.
7+
f `123qdawdrqw
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es6
2+
3+
function f(x: TemplateStringsArray, y: string, z: string) {
4+
}
5+
6+
// Incomplete call, not enough parameters, at EOF.
7+
f `
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es6
2+
3+
function f(x: TemplateStringsArray, y: string, z: string) {
4+
}
5+
6+
// Incomplete call, not enough parameters.
7+
f `123qdawdrqw${
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es6
2+
3+
function f(x: TemplateStringsArray, y: string, z: string) {
4+
}
5+
6+
// Incomplete call, enough parameters.
7+
f `123qdawdrqw${ }${
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es6
2+
3+
function f(x: TemplateStringsArray, y: string, z: string) {
4+
}
5+
6+
// Incomplete call, not enough parameters.
7+
f `123qdawdrqw${ 1 }${
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es6
2+
3+
function f(x: TemplateStringsArray, y: string, z: string) {
4+
}
5+
6+
// Incomplete call, but too many parameters.
7+
f `123qdawdrqw${ 1 }${ }${
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es6
2+
3+
function f(x: TemplateStringsArray, y: string, z: string) {
4+
}
5+
6+
// Incomplete call, but too many parameters.
7+
f `123qdawdrqw${ 1 }${ 2 }${
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es6
2+
3+
function f(x: TemplateStringsArray, y: string, z: string) {
4+
}
5+
6+
// Incomplete call, not enough parameters, at EOF.
7+
f `123qdawdrqw${ 1 }${

0 commit comments

Comments
 (0)